From abbc655b6ec1b73da7e11c853d2b4a059ae973c7 Mon Sep 17 00:00:00 2001 From: Doug Hilpipre Date: Thu, 26 Mar 2026 12:16:45 -0500 Subject: [PATCH 1/7] initial checkin of Reactor replacement instrumentation --- .../TokenLinkingSubscriber.java | 23 -- .../HttpServerHandler_Instrumentation.java | 8 - instrumentation/reactor-3.3.0/NOTICE.txt | 17 - instrumentation/reactor-3.3.0/README.md | 27 -- instrumentation/reactor-3.3.0/build.gradle | 12 - .../com/nr/instrumentation/ReactorConfig.java | 18 - .../instrumentation/SubscriptionWrapper.java | 37 -- .../reactor/TokenLinkingSubscriber.java | 114 ------ .../FluxMapFuseable_Instrumentation.java | 66 --- .../core/publisher/Hooks_Instrumentation.java | 25 -- .../LambdaMonoSubscriber_Instrumentation.java | 78 ---- .../LambdaSubscriber_Instrumentation.java | 77 ---- .../MonoSubscribeOn_Instrumentation.java | 49 --- ...antPeriodicWorkerTask_Instrumentation.java | 23 -- .../PeriodicWorkerTask_Instrumentation.java | 23 -- .../SchedulerTask_Instrumentation.java | 23 -- .../scheduler/Schedulers_Instrumentation.java | 38 -- .../scheduler/WorkerTask_Instrumentation.java | 23 -- .../http/server/HttpServerHandler_Skip.java | 15 - .../http/server/HttpTrafficHandler_Skip.java | 15 - .../TransactionPropagationTest.java | 358 ----------------- .../reactor-core-3.1.0/build.gradle | 12 + .../reactor/NRRunnableWrapper.java | 35 ++ .../reactor/ReactorConfig.java | 9 + .../instrumentation/reactor/ReactorUtils.java | 25 ++ .../publisher/FluxCreate_Instrumentation.java | 120 ++++++ .../publisher/MonoCreate_Instrumentation.java | 72 ++++ .../scheduler/Scheduler_Instrumentation.java | 51 +++ .../scheduler/Schedulers_Instrumentation.java | 44 ++ .../reactor/test/AwaitMany.java | 39 ++ .../reactor/test/AwaitSingle.java | 32 ++ .../reactor/test/MonoCoreSubscriber.java | 35 ++ .../reactor/test/SubscriptionConsumer.java | 23 ++ .../reactor/test/TestApplication.java | 377 ++++++++++++++++++ .../reactor/test/TestFluxCoreSubscriber.java | 52 +++ .../reactor/test/TestMonoCoreSubscriber.java | 50 +++ .../reactor-core-3.3.0/build.gradle | 12 + .../reactor/NRRunnableWrapper.java | 35 ++ .../instrumentation/reactor/ReactorUtils.java | 25 ++ .../publisher/FluxCreate_Instrumentation.java | 197 +++++++++ .../publisher/MonoCreate_Instrumentation.java | 69 ++++ .../scheduler/DirectScheduleTask_Skip.java | 7 + .../scheduler/Scheduler_Instrumentation.java | 51 +++ .../scheduler/Schedulers_Instrumentation.java | 45 +++ .../reactor/test/AwaitMany.java | 39 ++ .../reactor/test/AwaitSingle.java | 32 ++ .../reactor/test/MonoCoreSubscriber.java | 35 ++ .../reactor/test/SubscriptionConsumer.java | 23 ++ .../reactor/test/TestApplication.java | 332 +++++++++++++++ .../reactor/test/TestFluxCoreSubscriber.java | 52 +++ .../reactor/test/TestMonoCoreSubscriber.java | 50 +++ .../reactor-core-3.4.0/build.gradle | 12 + .../reactor/NRRunnableWrapper.java | 35 ++ .../instrumentation/reactor/ReactorUtils.java | 25 ++ .../EmitterProcessor_Instrumentation.java | 45 +++ .../publisher/FluxCreate_Instrumentation.java | 191 +++++++++ .../publisher/MonoCreate_Instrumentation.java | 70 ++++ .../NextProcessor_Instrumentation.java | 43 ++ .../ReplayProcessor_Instrumentation.java | 45 +++ .../SinkEmptyMulticast_Instrumentation.java | 39 ++ .../SinkEmptySerialized_Instrumentation.java | 23 ++ .../SinkManyBestEffort_Instrumentation.java | 46 +++ .../SinkManySerialized_Instrumentation.java | 46 +++ .../SinkOneSerialized_Instrumentation.java | 14 + ...anySinkNoBackpressure_Instrumentation.java | 49 +++ .../UnicastProcessor_Instrumentation.java | 63 +++ .../scheduler/DirectScheduleTask_Skip.java | 7 + .../scheduler/Scheduler_Instrumentation.java | 51 +++ .../scheduler/Schedulers_Instrumentation.java | 45 +++ .../reactor/test/AwaitMany.java | 37 ++ .../reactor/test/AwaitSingle.java | 32 ++ .../reactor/test/TestApplication.java | 168 ++++++++ .../reactor/test/TestFluxCoreSubscriber.java | 52 +++ .../reactor/test/TestMonoCoreSubscriber.java | 50 +++ .../reactor-core-3.4.10/build.gradle | 12 + .../reactor/NRRunnableWrapper.java | 35 ++ .../instrumentation/reactor/ReactorUtils.java | 25 ++ .../EmitterProcessor_Instrumentation.java | 45 +++ .../publisher/FluxCreate_Instrumentation.java | 191 +++++++++ .../publisher/MonoCreate_Instrumentation.java | 70 ++++ .../NextProcessor_Instrumentation.java | 37 ++ .../ReplayProcessor_Instrumentation.java | 45 +++ .../SinkEmptyMulticast_Instrumentation.java | 39 ++ .../SinkEmptySerialized_Instrumentation.java | 23 ++ .../SinkManyBestEffort_Instrumentation.java | 46 +++ .../SinkManySerialized_Instrumentation.java | 46 +++ .../SinkOneMulticast_Instrumentation.java | 19 + .../SinkOneSerialized_Instrumentation.java | 14 + ...anySinkNoBackpressure_Instrumentation.java | 49 +++ .../UnicastProcessor_Instrumentation.java | 63 +++ .../scheduler/DirectScheduleTask_Skip.java | 7 + .../scheduler/Scheduler_Instrumentation.java | 51 +++ .../scheduler/Schedulers_Instrumentation.java | 45 +++ .../reactor-core-3.5.0/build.gradle | 12 + .../reactor/NRRunnableWrapper.java | 35 ++ .../instrumentation/reactor/ReactorUtils.java | 25 ++ .../EmitterProcessor_Instrumentation.java | 45 +++ .../publisher/FluxCreate_Instrumentation.java | 191 +++++++++ .../publisher/MonoCreate_Instrumentation.java | 71 ++++ .../NextProcessor_Instrumentation.java | 37 ++ .../ReplayProcessor_Instrumentation.java | 45 +++ .../SinkEmptyMulticast_Instrumentation.java | 39 ++ .../SinkEmptySerialized_Instrumentation.java | 23 ++ .../SinkManyBestEffort_Instrumentation.java | 46 +++ .../SinkManySerialized_Instrumentation.java | 46 +++ .../SinkOneMulticast_Instrumentation.java | 19 + .../SinkOneSerialized_Instrumentation.java | 14 + .../UnicastManySinkNoBackpressure_Skip.java | 8 + .../UnicastProcessor_Instrumentation.java | 63 +++ .../scheduler/DirectScheduleTask_Skip.java | 7 + .../scheduler/Scheduler_Instrumentation.java | 51 +++ .../scheduler/Schedulers_Instrumentation.java | 45 +++ settings.gradle | 7 +- 113 files changed, 4858 insertions(+), 1070 deletions(-) delete mode 100644 instrumentation/reactor-3.3.0/NOTICE.txt delete mode 100644 instrumentation/reactor-3.3.0/README.md delete mode 100644 instrumentation/reactor-3.3.0/build.gradle delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/com/nr/instrumentation/ReactorConfig.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/com/nr/instrumentation/SubscriptionWrapper.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/com/nr/instrumentation/reactor/TokenLinkingSubscriber.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/FluxMapFuseable_Instrumentation.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/LambdaMonoSubscriber_Instrumentation.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/LambdaSubscriber_Instrumentation.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/MonoSubscribeOn_Instrumentation.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/reactor/ipc/netty/http/server/HttpServerHandler_Skip.java delete mode 100644 instrumentation/reactor-3.3.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java delete mode 100644 instrumentation/reactor-3.3.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java create mode 100644 instrumentation/reactor-core-3.1.0/build.gradle create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java create mode 100644 instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java create mode 100644 instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java create mode 100644 instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java create mode 100644 instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.3.0/build.gradle create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java create mode 100644 instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java create mode 100644 instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java create mode 100644 instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java create mode 100644 instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.4.0/build.gradle create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java create mode 100644 instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java create mode 100644 instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java create mode 100644 instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.4.10/build.gradle create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/build.gradle create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Skip.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java diff --git a/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/TokenLinkingSubscriber.java b/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/TokenLinkingSubscriber.java index 186e520b35..3c2d0d5097 100644 --- a/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/TokenLinkingSubscriber.java +++ b/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/TokenLinkingSubscriber.java @@ -74,27 +74,4 @@ private void withNRError(Runnable runnable, Throwable throwable) { runnable.run(); } - public static Function, ? extends Publisher> tokenLift() { - return Operators.lift(new TokenLifter<>()); - } - - private static class TokenLifter - implements BiFunction, CoreSubscriber> { - - public TokenLifter() { - } - - @Override - public CoreSubscriber apply(Scannable publisher, CoreSubscriber sub) { - // if Flux/Mono #just, #empty, #error - if (publisher instanceof Fuseable.ScalarCallable) { - return sub; - } - Token token = sub.currentContext().getOrDefault("newrelic-token", null); - if (token != null ) { - return new TokenLinkingSubscriber<>(sub, sub.currentContext()); - } - return sub; - } - } } \ No newline at end of file diff --git a/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/ipc/netty/http/server/HttpServerHandler_Instrumentation.java b/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/ipc/netty/http/server/HttpServerHandler_Instrumentation.java index 08d9f56289..380e77977d 100644 --- a/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/ipc/netty/http/server/HttpServerHandler_Instrumentation.java +++ b/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/ipc/netty/http/server/HttpServerHandler_Instrumentation.java @@ -11,21 +11,13 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.TokenLinkingSubscriber; import io.netty.channel.ChannelHandlerContext_Instrumentation; import io.netty.handler.codec.http.HttpRequest; -import reactor.core.publisher.Hooks; -import reactor.core.publisher.Hooks_Instrumentation; - -import static com.nr.instrumentation.TokenLinkingSubscriber.tokenLift; @Weave(type = MatchType.BaseClass, originalName = "reactor.ipc.netty.http.server.HttpServerHandler") class HttpServerHandler_Instrumentation { public void channelRead(ChannelHandlerContext_Instrumentation ctx, Object msg) { - if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { - Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); - } Weaver.callOriginal(); if (msg instanceof HttpRequest) { if (ctx.pipeline().reactiveLayerToken == null) { diff --git a/instrumentation/reactor-3.3.0/NOTICE.txt b/instrumentation/reactor-3.3.0/NOTICE.txt deleted file mode 100644 index 2a0270a9c2..0000000000 --- a/instrumentation/reactor-3.3.0/NOTICE.txt +++ /dev/null @@ -1,17 +0,0 @@ -This product contains a modified part of OpenTelemetry: - - * License: - -Copyright 2019 The OpenTelemetry 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. - - * Homepage: https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/LICENSE diff --git a/instrumentation/reactor-3.3.0/README.md b/instrumentation/reactor-3.3.0/README.md deleted file mode 100644 index 3c0a851b3f..0000000000 --- a/instrumentation/reactor-3.3.0/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Reactor Instrumentation - -Instrumentation for Reactor Core library code. This module provides mostly the same functionality of `netty-reactor-0.9.0`, but this will only apply when netty -is not being used. - -This instrumentation module is a subset of the `netty-reactor-0.9.0` instrumentation. It does not contain anything related to HTTP nor starting transactions and has added Skips for when `reactor-netty` classes are present. - -Changes to `netty-reactor` should be mirrored here and vice-versa. - -## Why not separate the functionality? -`netty-reactor` modules register the `tokenLift` in the Hooks class from two different code paths, one from reactor and another from netty. To make sure that -`tokenLift` is registered only once, an AtomicBoolean new field is added to the `Hooks` class. Before registering, that field is checked and if false, -`tokenLift` is registered and the field is set to true. - -The code cannot be separated in modules because the new field is not visible across modules, so there would be no way to make sure that `tokenLift` gets -registered only once. - -## Notice -This module will only properly link the code if the Mono/Flux is subscribed on a scheduler. - -Example: -``` -Flux.just(1, 2, 3) - .map(i -> doSomething(i)) - .subscribeOn(Schedulers.parallel()) - .subscribe(); -``` diff --git a/instrumentation/reactor-3.3.0/build.gradle b/instrumentation/reactor-3.3.0/build.gradle deleted file mode 100644 index bdd73f0d36..0000000000 --- a/instrumentation/reactor-3.3.0/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -dependencies { - implementation(project(":agent-bridge")) - implementation("io.projectreactor:reactor-core:3.3.21.RELEASE") -} - -jar { - manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.reactor-3.3.0' } -} - -verifyInstrumentation { - passesOnly 'io.projectreactor:reactor-core:[3.3.0.RELEASE,)' -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/com/nr/instrumentation/ReactorConfig.java b/instrumentation/reactor-3.3.0/src/main/java/com/nr/instrumentation/ReactorConfig.java deleted file mode 100644 index 0c57943f39..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/com/nr/instrumentation/ReactorConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * - * * Copyright 2023 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package com.nr.instrumentation; - -import com.newrelic.api.agent.NewRelic; - -public class ReactorConfig { - public static final boolean errorsEnabled = NewRelic.getAgent().getConfig() - .getValue("reactor.errors.enabled", false); - - private ReactorConfig() { - } -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/com/nr/instrumentation/SubscriptionWrapper.java b/instrumentation/reactor-3.3.0/src/main/java/com/nr/instrumentation/SubscriptionWrapper.java deleted file mode 100644 index 4d011a81f7..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/com/nr/instrumentation/SubscriptionWrapper.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * * Copyright 2026 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package com.nr.instrumentation; - -import com.newrelic.api.agent.Token; -import org.reactivestreams.Subscription; -import reactor.util.context.Context; - -public class SubscriptionWrapper implements Subscription { - - Subscription delegate; - Context currentContext; - - public SubscriptionWrapper(Subscription delegate, Context context) { - this.delegate = delegate; - this.currentContext = context; - } - - @Override - public void request(long n) { - delegate.request(n); - } - - @Override - public void cancel() { - Token token = currentContext.getOrDefault("newrelic-token", null); - if (token != null) { - token.linkAndExpire(); - } - if (delegate != null) delegate.cancel(); - } -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/com/nr/instrumentation/reactor/TokenLinkingSubscriber.java b/instrumentation/reactor-3.3.0/src/main/java/com/nr/instrumentation/reactor/TokenLinkingSubscriber.java deleted file mode 100644 index af08ac44ce..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/com/nr/instrumentation/reactor/TokenLinkingSubscriber.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * - * * Copyright 2023 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package com.nr.instrumentation.reactor; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; -import com.nr.instrumentation.ReactorConfig; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; -import reactor.core.CoreSubscriber; -import reactor.core.Fuseable; -import reactor.core.Scannable; -import reactor.core.publisher.Operators; -import reactor.util.context.Context; - -import java.util.function.BiFunction; -import java.util.function.Function; - -/** - * Implementation of a reactor.core.CoreSubscriber (a Context aware subscriber) that can be added as - * a lifecycle hook on Flux/Mono operators to propagate, retrieve, and link Tokens across async contexts. - * - * Based on OpenTelemetry code: - * https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/TracingSubscriber.java - * @param - */ -public class TokenLinkingSubscriber implements CoreSubscriber { - private final Token token; - private final Subscriber subscriber; - private Context context; - - public TokenLinkingSubscriber(Subscriber subscriber, Context ctx) { - this.subscriber = subscriber; - this.context = ctx; - // newrelic-token is added by spring-webflux instrumentation of ServerWebExchange - this.token = ctx.getOrDefault("newrelic-token", null); - } - - @Override - public void onSubscribe(Subscription subscription) { - withNRToken(() -> subscriber.onSubscribe(subscription)); - } - - @Override - public void onNext(T o) { - withNRToken(() -> subscriber.onNext(o)); - } - - @Override - public void onError(Throwable throwable) { - withNRError(() -> subscriber.onError(throwable), throwable); - } - - @Override - public void onComplete() { - withNRToken(subscriber::onComplete); - } - - @Override - public Context currentContext() { - return context; - } - - @Trace(async = true, excludeFromTransactionTrace = true) - private void withNRToken(Runnable runnable) { - if (token != null && AgentBridge.getAgent().getTransaction(false) == null) { - token.link(); - } - runnable.run(); - } - - @Trace(async = true, excludeFromTransactionTrace = true) - private void withNRError(Runnable runnable, Throwable throwable) { - if (token != null && token.isActive()) { - token.link(); - if (ReactorConfig.errorsEnabled) { - NewRelic.noticeError(throwable); - } - } - runnable.run(); - } - - public static Function, ? extends Publisher> tokenLift() { - return Operators.lift(new TokenLifter<>()); - } - - private static class TokenLifter - implements BiFunction, CoreSubscriber> { - - public TokenLifter() { - } - - @Override - public CoreSubscriber apply(Scannable publisher, CoreSubscriber sub) { - // if Flux/Mono #just, #empty, #error - if (publisher instanceof Fuseable.ScalarCallable) { - return sub; - } - Token token = sub.currentContext().getOrDefault("newrelic-token", null); - if (token != null ) { - return new TokenLinkingSubscriber<>(sub, sub.currentContext()); - } - return sub; - } - } -} \ No newline at end of file diff --git a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/FluxMapFuseable_Instrumentation.java b/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/FluxMapFuseable_Instrumentation.java deleted file mode 100644 index 164d927fbd..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/FluxMapFuseable_Instrumentation.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * - * * Copyright 2024 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.publisher; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.WeaveAllConstructors; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(type = MatchType.ExactClass, originalName = "reactor.core.publisher.FluxMapFuseable") -abstract class FluxMapFuseable_Instrumentation { - - @Weave(type = MatchType.ExactClass, originalName = "reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber") - static final class MapFuseableSubscriber_Instrumentation { - @NewField - private Token token; - - @WeaveAllConstructors - MapFuseableSubscriber_Instrumentation() { - if (AgentBridge.getAgent().getTransaction(false) != null && token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - public void onComplete() { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - - public void onNext(T t) { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - - public void onError(Throwable t) { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - - public void cancel() { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - } -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java b/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java deleted file mode 100644 index e6f0b58502..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * - * * Copyright 2023 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.publisher; - -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; - -import java.util.concurrent.atomic.AtomicBoolean; - -@Weave(originalName = "reactor.core.publisher.Hooks") -public abstract class Hooks_Instrumentation { - - /* - * Note that sub-hooks are cumulative. We want to avoid setting the same sub-hooks - * more than once, so we set this boolean to true the first time we set a sub-hook. - * if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { Hooks.onEachOperator(...) } - */ - @NewField - public static AtomicBoolean instrumented = new AtomicBoolean(false); -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/LambdaMonoSubscriber_Instrumentation.java b/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/LambdaMonoSubscriber_Instrumentation.java deleted file mode 100644 index b83e609d63..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/LambdaMonoSubscriber_Instrumentation.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * * Copyright 2023 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.publisher; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.WeaveAllConstructors; -import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.SubscriptionWrapper; -import org.reactivestreams.Subscription; -import reactor.util.context.Context; - -@Weave(originalName = "reactor.core.publisher.LambdaMonoSubscriber") -abstract class LambdaMonoSubscriber_Instrumentation { - @NewField - private Context nrContext; - final Context initialContext = Weaver.callOriginal(); - - @WeaveAllConstructors - protected LambdaMonoSubscriber_Instrumentation() { - // LamdaMonoSubscriber creates a new Context, so we create a new token and put it on the Context - // to be linked by TokenLinkingSubscriber but expired on onComplete here - if (AgentBridge.getAgent().getTransaction(false) != null - && initialContext.getOrDefault("newrelic-token", null) == null) { - nrContext = Context.of("newrelic-token", NewRelic.getAgent().getTransaction().getToken()); - } - } - - public final void onComplete() { - Token token = this.currentContext().getOrDefault("newrelic-token", null); - if (token != null) { - token.expire(); - this.nrContext = null; - } - Weaver.callOriginal(); - } - - public final void onError(Throwable t) { - Token token = this.currentContext().getOrDefault("newrelic-token", null); - if (token != null) { - token.expire(); - this.nrContext = null; - } - Weaver.callOriginal(); - } - - public void dispose() { - Token token = this.currentContext().getOrDefault("newrelic-token", null); - if (token != null) { - token.expire(); - this.nrContext = null; - } - Weaver.callOriginal(); - } - - public final void onSubscribe(Subscription s) { - if (s != null) { - s = new SubscriptionWrapper(s, this.currentContext()); - } - Weaver.callOriginal(); - } - - public Context currentContext() { - if (nrContext != null) { - return initialContext.putAll(nrContext); - } - return Weaver.callOriginal(); - } - -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/LambdaSubscriber_Instrumentation.java b/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/LambdaSubscriber_Instrumentation.java deleted file mode 100644 index e136dc0b52..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/LambdaSubscriber_Instrumentation.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * * Copyright 2023 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.publisher; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.WeaveAllConstructors; -import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.SubscriptionWrapper; -import org.reactivestreams.Subscription; -import reactor.util.context.Context; - -@Weave(originalName = "reactor.core.publisher.LambdaSubscriber") -abstract class LambdaSubscriber_Instrumentation { - final Context initialContext = Weaver.callOriginal(); - @NewField - private Context nrContext; - - @WeaveAllConstructors - protected LambdaSubscriber_Instrumentation() { - if (AgentBridge.getAgent().getTransaction(false) != null - && initialContext.getOrDefault("newrelic-token", null) == null) { - nrContext = Context.of("newrelic-token", NewRelic.getAgent().getTransaction().getToken()); - } - } - - public final void onComplete() { - Token token = this.currentContext().getOrDefault("newrelic-token", null); - if (token != null) { - token.expire(); - this.nrContext = null; - } - Weaver.callOriginal(); - } - - public final void onError(Throwable t) { - Token token = this.currentContext().getOrDefault("newrelic-token", null); - if (token != null) { - token.expire(); - this.nrContext = null; - } - Weaver.callOriginal(); - } - - public void dispose() { - Token token = this.currentContext().getOrDefault("newrelic-token", null); - if (token != null) { - token.expire(); - this.nrContext = null; - } - Weaver.callOriginal(); - } - - public final void onSubscribe(Subscription s) { - if (s != null) { - s = new SubscriptionWrapper(s, this.currentContext()); - } - Weaver.callOriginal(); - } - - public Context currentContext() { - if (nrContext != null) { - //return nrContext; - return initialContext.putAll(nrContext); - } - return Weaver.callOriginal(); - } - -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/MonoSubscribeOn_Instrumentation.java b/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/MonoSubscribeOn_Instrumentation.java deleted file mode 100644 index 92dd50ef4b..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/publisher/MonoSubscribeOn_Instrumentation.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * * Copyright 2024 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.publisher; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.WeaveAllConstructors; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(type = MatchType.ExactClass, originalName = "reactor.core.publisher.MonoSubscribeOn") -abstract class MonoSubscribeOn_Instrumentation { - - @Weave(type = MatchType.ExactClass, originalName = "reactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber") - static final class SubscribeOnSubscriber_Instrumentation { - @NewField - private Token token; - - @WeaveAllConstructors - SubscribeOnSubscriber_Instrumentation() { - if (NewRelic.getAgent().getTransaction() != null && token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - public void run () { - if (token != null) { - Boolean result = token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - - public void cancel () { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - } -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java b/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java deleted file mode 100644 index 4ee5d5c532..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - * * Copyright 2023 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(originalName = "reactor.core.scheduler.InstantPeriodicWorkerTask") -final class InstantPeriodicWorkerTask_Instrumentation { - - // We need to be able to link the Token here when executing on a supplied Scheduler - // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator - @Trace(async = true, excludeFromTransactionTrace = true) - public Void call() { - return Weaver.callOriginal(); - } -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java b/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java deleted file mode 100644 index ad01576c16..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - * * Copyright 2023 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(originalName = "reactor.core.scheduler.PeriodicWorkerTask") -final class PeriodicWorkerTask_Instrumentation { - - // We need to be able to link the Token here when executing on a supplied Scheduler - // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator - @Trace(async = true, excludeFromTransactionTrace = true) - public Void call() { - return Weaver.callOriginal(); - } -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java b/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java deleted file mode 100644 index af818096b6..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - * * Copyright 2023 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(originalName = "reactor.core.scheduler.SchedulerTask") -final class SchedulerTask_Instrumentation { - - // We need to be able to link the Token here when executing on a supplied Scheduler via Mono::publishOn - // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator - @Trace(async = true, excludeFromTransactionTrace = true) - public Void call() { - return Weaver.callOriginal(); - } -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java b/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java deleted file mode 100644 index 9e7a1bbf0e..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2023 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.reactor.TokenLinkingSubscriber; -import reactor.core.publisher.Hooks; -import reactor.core.publisher.Hooks_Instrumentation; - -import static com.nr.instrumentation.reactor.TokenLinkingSubscriber.tokenLift; - -@Weave(type = MatchType.BaseClass, originalName = "reactor.core.scheduler.Schedulers") -public abstract class Schedulers_Instrumentation { - - @Weave(type = MatchType.ExactClass, originalName = "reactor.core.scheduler.Schedulers$CachedScheduler") - static class CachedScheduler { - CachedScheduler(String key, Scheduler cached) { - /* - * Add tokenLift hook if it hasn't already been added. This allows for tokens to be retrieved from - * the current context and linked across threads at various points of the Flux/Mono lifecycle. - * - * When using Netty Reactor with SpringBoot this hook will be added by the HttpTrafficHandler_Instrumentation - * but when using other embedded web servers (e.g. Tomcat, Jetty, Undertow) the HttpTrafficHandler class - * doesn't get loaded and thus the hook isn't added. This ensures that the hook is added in a common code - * path before any Scheduler Tasks are spun off on new threads. - */ - if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { - Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); - } - } - } - -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java b/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java deleted file mode 100644 index 2e51fdaf22..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - * * Copyright 2023 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(originalName = "reactor.core.scheduler.WorkerTask") -final class WorkerTask_Instrumentation { - - // We need to be able to link the Token here when executing on a supplied Scheduler - // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator - @Trace(async = true, excludeFromTransactionTrace = true) - public Void call() { - return Weaver.callOriginal(); - } -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/reactor/ipc/netty/http/server/HttpServerHandler_Skip.java b/instrumentation/reactor-3.3.0/src/main/java/reactor/ipc/netty/http/server/HttpServerHandler_Skip.java deleted file mode 100644 index 314f8b7432..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/reactor/ipc/netty/http/server/HttpServerHandler_Skip.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * - * * Copyright 2023 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.ipc.netty.http.server; - -import com.newrelic.api.agent.weaver.SkipIfPresent; - -// prevents this module from applying when reactor-netty 0.7.0 is present -@SkipIfPresent(originalName = "reactor.ipc.netty.http.server.HttpServerHandler") -class HttpServerHandler_Skip { -} diff --git a/instrumentation/reactor-3.3.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java b/instrumentation/reactor-3.3.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java deleted file mode 100644 index 4831e7c131..0000000000 --- a/instrumentation/reactor-3.3.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * - * * Copyright 2023 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.netty.http.server; - -import com.newrelic.api.agent.weaver.SkipIfPresent; - -// prevents this module from applying when reactor-netty 0.8.0 is present -@SkipIfPresent(originalName = "reactor.netty.http.server.HttpTrafficHandler") -class HttpTrafficHandler_Skip { -} diff --git a/instrumentation/reactor-3.3.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java b/instrumentation/reactor-3.3.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java deleted file mode 100644 index ec0aff4806..0000000000 --- a/instrumentation/reactor-3.3.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - * - * * Copyright 2023 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package com.nr.agent.instrumentation; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.agent.bridge.Token; -import com.newrelic.agent.introspec.InstrumentationTestConfig; -import com.newrelic.agent.introspec.InstrumentationTestRunner; -import com.newrelic.agent.introspec.Introspector; -import com.newrelic.api.agent.Trace; -import com.nr.instrumentation.reactor.TokenLinkingSubscriber; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Hooks; -import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; -import reactor.util.context.Context; - -import java.time.Duration; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; - -import static com.nr.instrumentation.reactor.TokenLinkingSubscriber.tokenLift; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertTrue; - -@SuppressWarnings("deprecation") -@RunWith(InstrumentationTestRunner.class) -@InstrumentationTestConfig(includePrefixes = {"reactor"}) -public class TransactionPropagationTest { - - public static final String A_VALUE = ""; - - @BeforeClass - public static void init() { - Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); - } - - @Test - public void syncPropagationSanityCheck() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> - checkTransaction(hadTransaction)); - assertCapturedData(hadTransaction); - } - - @Test - public void asyncPropagationSanityCheck() throws InterruptedException { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - inAnotherThread(() -> - inAnnotatedWithTraceAsync(() -> { - token.linkAndExpire(); - checkTransaction(hadTransaction); - done.countDown(); - }) - ); - }); - done.await(); - assertCapturedData(hadTransaction); - } - - @Test - public void testReactorSchedulersInstrumentation() throws InterruptedException { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - Schedulers.elastic().schedule(() -> { -// trace_async(() -> { it is not need as Tasks are instrumented and annotated with @Trace(async = ture) - token.linkAndExpire(); - checkTransaction(hadTransaction); - done.countDown(); -// }); - }); - }); - done.await(); - assertCapturedData(hadTransaction); - } - - @Test - public void testEmptyMonoOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - Mono.empty() - .subscribeOn(Schedulers.elastic()) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testEmptyFluxOnComplete() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - Flux.empty() - .subscribeOn(Schedulers.elastic()) - .doOnComplete(() -> - checkTransaction(hadTransaction)) - .subscriberContext(with(token)) - .blockFirst(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testMonoOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - Mono.just(A_VALUE) - .subscribeOn(Schedulers.elastic()) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testMonoRetryOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - AtomicBoolean firstCall = new AtomicBoolean(true); - Mono - .create(monoSink -> - inAnotherThread(() -> { - if (firstCall.getAndSet(false)) - monoSink.error(new RuntimeException("failing the first call")); - else - monoSink.success(A_VALUE); - }) - ) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - .retry(2) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testMonoRetryBackoffOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - AtomicBoolean firstCall = new AtomicBoolean(true); - Mono - .create(monoSink -> - inAnotherThread(() -> { - if (firstCall.getAndSet(false)) - monoSink.error(new RuntimeException("failing the first call")); - else - monoSink.success(A_VALUE); - }) - ) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - .retryBackoff(2, Duration.ZERO) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testMonoNestedInFlatMap() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - Mono.just(A_VALUE) - .subscribeOn(Schedulers.elastic()) - .flatMap(v -> - Mono.just(A_VALUE) - .subscribeOn(Schedulers.elastic()) - .doOnSuccess(v2 -> - checkTransaction(hadTransaction))) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test(timeout = 10000L) - public void testLambdaMonoSubscriberOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - Mono.empty() - .subscribeOn(Schedulers.elastic()) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - - // it is not need as LambdaMonoSubscriber instrumentation creates token - // and puts it into the context - //.subscriberContext(with(token)) - - // Call countDown in onComplete to see that instrumentation code calls original method - .subscribe(nil(), nil(), done::countDown); - await(done); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test(timeout = 10000L) - public void testLambdaMonoSubscriberOnError() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - Mono.error(new RuntimeException()) - .subscribeOn(Schedulers.elastic()) - .doOnError(v -> - checkTransaction(hadTransaction)) - - // it is not need as LambdaMonoSubscriber instrumentation creates token - // and puts it into the context - //.subscriberContext(with(token)) - - // Call countDown in onError to see that instrumentation code calls original method - .subscribe(nil(), v -> done.countDown()); - await(done); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test(timeout = 10000L) - public void testLambdaSubscriberOnComplete() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - Flux.empty() - .subscribeOn(Schedulers.elastic()) - .doOnComplete(() -> - checkTransaction(hadTransaction)) - - // it is not need as LambdaSubscriber instrumentation creates token - // and puts it into the context - //.subscriberContext(with(token)) - - // Call countDown in onComplete to see that instrumentation code calls original method - .subscribe(nil(), nil(), done::countDown); - await(done); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test(timeout = 10000L) - public void testLambdaSubscriberOnError() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - Flux.error(new RuntimeException()) - .subscribeOn(Schedulers.elastic()) - .doOnError(v -> - checkTransaction(hadTransaction)) - - // it is not need as LambdaSubscriber instrumentation creates token - // and puts it into the context - //.subscriberContext(with(token)) - - // Call countDown in onError to see that instrumentation code calls original method - .subscribe(nil(), v -> done.countDown()); - await(done); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Trace(dispatcher = true) - public void inTransaction(Runnable actions) { - actions.run(); - } - - public void inAnotherThread(Runnable runnable) { - new Thread(runnable).start(); - } - - @Trace(async = true) - public void inAnnotatedWithTraceAsync(Runnable runnable) { - runnable.run(); - } - - public Token createToken() { - return AgentBridge.getAgent().getTransaction(false).getToken(); - } - - public Context with(Token token) { - return Context.empty().put("newrelic-token", token); - } - - @Trace - public void checkTransaction(AtomicBoolean hadTransaction) { - hadTransaction.set(AgentBridge.getAgent().getTransaction(false) != null); - } - - private Consumer nil() { - return v -> { - }; - } - - private void await(CountDownLatch done) { - try { - done.await(); - } catch (InterruptedException ignore) { - } - } - - private void assertCapturedData(AtomicBoolean hadTransaction) { - assertTrue("Did not have transaction", hadTransaction.get()); - - Introspector introspector = InstrumentationTestRunner.getIntrospector(); - - assertThat("No finished transactions", introspector.getFinishedTransactionCount(), - is(greaterThan(0))); - - assertThat("Transaction names", introspector.getTransactionNames(), contains( - "OtherTransaction/Custom/" + getClass().getName() + "/inTransaction" - )); - - assertThat("Unscoped metrics", introspector.getUnscopedMetrics().keySet(), hasItems( - "Java/" + getClass().getName() + "/inTransaction", - "Custom/" + getClass().getName() + "/checkTransaction" - )); - } -} diff --git a/instrumentation/reactor-core-3.1.0/build.gradle b/instrumentation/reactor-core-3.1.0/build.gradle new file mode 100644 index 0000000000..1b20f6f987 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/build.gradle @@ -0,0 +1,12 @@ +dependencies { + implementation(project(":agent-bridge")) + implementation("io.projectreactor:reactor-core:3.1.0.RELEASE") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.reactor-core-3.1.0' } +} + +verifyInstrumentation { + passesOnly 'io.projectreactor:reactor-core:[3.1.0.RELEASE,3.3.0.RELEASE)' +} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java b/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java new file mode 100644 index 0000000000..ffb4a47ac8 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java @@ -0,0 +1,35 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; + +public class NRRunnableWrapper implements Runnable { + + private Runnable delegate = null; + + private Token token = null; + private static boolean isTransformed = false; + + public NRRunnableWrapper(Runnable r, Token t) { + delegate = r; + token = t; + if(!isTransformed) { + isTransformed = true; + AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); + } + } + + @Override + @Trace(async=true) + public void run() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + if(delegate != null) { + delegate.run(); + } + } + +} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java b/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java new file mode 100644 index 0000000000..377beab791 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java @@ -0,0 +1,9 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.api.agent.NewRelic; + +public class ReactorConfig { + public static final boolean errorsEnabledNetty = NewRelic.getAgent().getConfig().getValue("reactor-netty.errors.enabled", false); + public static final boolean errorsEnabled = NewRelic.getAgent().getConfig().getValue("reactor.errors.enabled", false); + +} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java b/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java new file mode 100644 index 0000000000..8538825ebe --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java @@ -0,0 +1,25 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; + +public class ReactorUtils { + + public static NRRunnableWrapper getRunnableWrapper(Runnable r) { + if(r instanceof NRRunnableWrapper) {return null;} + + Token currentToken = NewRelic.getAgent().getTransaction().getToken(); + if(currentToken != null) { + if(currentToken.isActive()) { + return new NRRunnableWrapper(r, currentToken); + } else { + currentToken.expire(); + currentToken = null; + return null; + } + } + + return null; + } + +} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java new file mode 100644 index 0000000000..ee36a8e34a --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -0,0 +1,120 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; +import reactor.core.CoreSubscriber; + +@Weave(originalName = "reactor.core.publisher.FluxCreate") +class FluxCreate_Instrumentation { + + @Weave(originalName = "reactor.core.publisher.FluxCreate$BaseSink", type = MatchType.BaseClass) + static abstract class BaseSink_Instrumentation implements FluxSink { + + @NewField + protected Token token = null; + + BaseSink_Instrumentation(CoreSubscriber actual) { + if(token == null) { + Token t = NewRelic.getAgent().getTransaction().getToken(); + if(t != null) { + if(!t.isActive()) { + token = t; + } else { + t.expire(); + t = null; + } + } + } + } + + @Trace(async=true) + public void complete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async=true) + public void error(Throwable e) { + if(ReactorConfig.errorsEnabled || ReactorConfig.errorsEnabledNetty) { + NewRelic.noticeError(e); + } + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$BufferAsyncSink") + static abstract class BufferAsyncSink_Instrumentation extends BaseSink_Instrumentation { + BufferAsyncSink_Instrumentation(CoreSubscriber actual, int capacityHint) { + super(actual); + } + + @Trace(async=true) + public FluxSink next(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$IgnoreSink") + static abstract class IgnoreSink_Instrumentation extends BaseSink_Instrumentation { + IgnoreSink_Instrumentation(CoreSubscriber actual) { + super(actual); + } + + @Trace(async=true) + public FluxSink next(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$LatestAsyncSink") + static abstract class LatestAsyncSink_Instrumentation extends BaseSink_Instrumentation { + LatestAsyncSink_Instrumentation(CoreSubscriber actual) { + super(actual); + } + + @Trace(async=true) + public FluxSink next(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$NoOverflowBaseAsyncSink") + static abstract class NoOverflowBaseAsyncSink_Instrumentation extends BaseSink_Instrumentation { + NoOverflowBaseAsyncSink_Instrumentation(CoreSubscriber actual) { + super(actual); + } + + @Trace(async=true) + public FluxSink next(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + +} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java new file mode 100644 index 0000000000..b38128da99 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -0,0 +1,72 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; +import reactor.core.CoreSubscriber; + +@Weave(originalName = "reactor.core.publisher.MonoCreate") +class MonoCreate_Instrumentation { + + @Weave(originalName = "reactor.core.publisher.MonoCreate$DefaultMonoSink") + static class DefaultMonoSink_Instrumentation { + + @NewField + private Token token = null; + + DefaultMonoSink_Instrumentation(CoreSubscriber actual) { + Token t = NewRelic.getAgent().getTransaction().getToken(); + if(t != null && t.isActive()) { + token = t; + } else if(t != null) { + t.expire(); + t = null; + } + } + + @Trace(async=true) + public void success() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async=true) + public void success(T value) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async=true) + public void error(Throwable e) { + if(ReactorConfig.errorsEnabled || ReactorConfig.errorsEnabledNetty) { + NewRelic.noticeError(e); + } + if(token != null) { + token.linkAndExpire(); + token = null; + } + NewRelic.noticeError(e); + Weaver.callOriginal(); + } + + @Trace(async=true) + public void cancel() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + } + +} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java new file mode 100644 index 0000000000..7b548210b3 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java @@ -0,0 +1,51 @@ +package reactor.core.scheduler; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.NRRunnableWrapper; +import com.nr.instrumentation.reactor.ReactorUtils; +import reactor.core.Disposable; + +import java.util.concurrent.TimeUnit; + +@Weave(originalName = "reactor.core.scheduler.Scheduler", type = MatchType.Interface) +public class Scheduler_Instrumentation { + + public Disposable schedule(Runnable task) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + public Disposable schedule(Runnable task, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + @Weave(originalName = "reactor.core.scheduler.Scheduler$Worker", type = MatchType.Interface) + public static class Worker_Instrumentation { + + public Disposable schedule(Runnable task) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + public Disposable schedule(Runnable task, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + } +} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java new file mode 100644 index 0000000000..7ab5b3bcfe --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java @@ -0,0 +1,44 @@ +package reactor.core.scheduler; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.NRRunnableWrapper; +import com.nr.instrumentation.reactor.ReactorUtils; + +import reactor.core.Disposable; + +@Weave(originalName = "reactor.core.scheduler.Schedulers") +public class Schedulers_Instrumentation { + + @Trace + static Disposable directSchedule(ScheduledExecutorService exec, Runnable task, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if(wrapper != null) { + task = wrapper; + } + + return Weaver.callOriginal(); + } + + public static Scheduler single() { + return Weaver.callOriginal(); + } + + @Trace + static Disposable workerSchedule(ScheduledExecutorService exec, + Disposable.Composite tasks, + Runnable task, + long delay, + TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if(wrapper != null) { + task = wrapper; + } + + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java new file mode 100644 index 0000000000..394d4e5651 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java @@ -0,0 +1,39 @@ +package com.nr.instrumentation.reactor.test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class AwaitMany { + + private List result = null; + private CompletableFuture> f; + + public AwaitMany() { + f = new CompletableFuture<>(); + } + + public List await() { + List s = Collections.emptyList(); + try { + s = f.get(); + } catch (InterruptedException | ExecutionException ignored) { + } + return s; + } + + public void onError(Throwable t) { + result.add(t.getMessage()); + f.completeExceptionally(t); + } + + public void done(List result) { + System.out.println("AwaitMany done"); + this.result = result; + f.complete(result); + } + +} diff --git a/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java new file mode 100644 index 0000000000..44434be273 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java @@ -0,0 +1,32 @@ +package com.nr.instrumentation.reactor.test; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class AwaitSingle { + + private String result = null; + private CompletableFuture f; + + public AwaitSingle() { + f = new CompletableFuture(); + } + + public String getResult() { + return result; + } + + public String await() { + String s = null; + try { + s = f.get(); + } catch (InterruptedException | ExecutionException ignored) { + } + return s; + } + + public void setResult(String s) { + result = s; + f.complete(result); + } +} diff --git a/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java new file mode 100644 index 0000000000..ceddfe3b71 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java @@ -0,0 +1,35 @@ +package com.nr.instrumentation.reactor.test; + +import org.reactivestreams.Subscription; + +import com.newrelic.api.agent.Trace; + +import reactor.core.CoreSubscriber; + +public class MonoCoreSubscriber implements CoreSubscriber { + + @Override + @Trace + public void onNext(String t) { + System.out.println("Received string for onNext: " + t); + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Received error for onError: " + t); + } + + @Override + @Trace + public void onComplete() { + System.out.println("Mono has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription var1) { + System.out.println("Mono was subscribed to by : " + var1); + } + +} diff --git a/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java new file mode 100644 index 0000000000..b3a2dce717 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java @@ -0,0 +1,23 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; + +import java.util.function.Consumer; + +public class SubscriptionConsumer implements Consumer { + + @Override + @Trace + public void accept(Subscription subscription) { + pause(); + + } + + private void pause() { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + } +} diff --git a/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java new file mode 100644 index 0000000000..39f17d4101 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java @@ -0,0 +1,377 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.agent.introspec.*; +import com.newrelic.api.agent.Trace; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = "reactor.core") +public class TestApplication { + + private static final String txn1 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testMonoSub"; + private static final String txn2 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testMonoPub"; + private static final String txn3 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testFluxSub"; + private static final String txn4 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testFluxPub"; + private static final String WRAPPER = "Java/com.nr.instrumentation.reactor.NRRunnableWrapper/run"; + private static final String MONO_NEXT = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onNext"; + private static final String MONO_COMPLETE = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onComplete"; + private static final String MONO_SUBSCRIBE = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onSubscribe"; + private static final String[] fluxArray = {"Message 1", "Message 2", "Message 3", "Message 4"}; + + @Test + public void doMonoSubscribeOnTest() { + testMonoSub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transactions", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn1); // & txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn1); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + Collection traces = introspector.getTransactionTracesForTransaction(txn1); + System.out.println("Transaction traces: " + traces.size()); + for (TransactionTrace transactionTrace : traces) { + TraceSegment initialSegment = transactionTrace.getInitialTraceSegment(); + List children = initialSegment.getChildren(); + boolean passes = false; + /* + Assure that the subscribing occurs on another thread + */ + for (TraceSegment child : children) { + if (child.getName().equals(WRAPPER)) { + Map initialAttributes = initialSegment.getTracerAttributes(); + int initialThread; + if (initialAttributes.containsKey("thread.id")) { + initialThread = Integer.parseInt(initialAttributes.get("thread.id").toString()); + } else { + initialThread = -1; + } + Map attributes = child.getTracerAttributes(); + int childThread; + if (attributes.containsKey("thread.id")) { + childThread = Integer.parseInt(attributes.get("thread.id").toString()); + } else { + childThread = -1; + } + Assert.assertTrue(initialThread != childThread); + passes = true; + break; + } + } + Assert.assertTrue(passes); + + } + } + + @Test + public void doMonoPublishOnTest() { + testMonoPub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn2); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + Collection traces = introspector.getTransactionTracesForTransaction(txn2); + System.out.println("Transaction traces: " + traces.size()); + for(TransactionTrace transactionTrace : traces) { + TraceSegment initialSegment = transactionTrace.getInitialTraceSegment(); + List children = initialSegment.getChildren(); + boolean passes = false; + /* + Assure that the publishing occurs on another thread + */ + for(TraceSegment child : children) { + if(child.getName().equals(WRAPPER)) { + Map initialAttributes = initialSegment.getTracerAttributes(); + int initialThread; + if(initialAttributes.containsKey("thread.id")) { + initialThread = Integer.parseInt(initialAttributes.get("thread.id").toString()); + } else { + initialThread = -1; + } + Map attributes = child.getTracerAttributes(); + int childThread; + if(attributes.containsKey("thread.id")) { + childThread = Integer.parseInt(attributes.get("thread.id").toString()); + } else { + childThread = -1; + } + Assert.assertTrue(initialThread != childThread); + passes = true; + } + } + Assert.assertTrue(passes); + + } + + } + + @Test + public void doMonoSinkTest() { + monoSinkTest(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + String txnName = txnNames.iterator().next(); + + Map metrics = introspector.getMetricsForTransaction(txnName); + Set names = metrics.keySet(); + // indicates that execution went to another thread + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + } + + @Trace(dispatcher = true) + public void monoSinkTest() { + Mono delayedMono = Mono.create(sink -> { + // Schedule an operation to emit a value after a delay + Scheduler scheduler = Schedulers.single(); + scheduler.schedule(() -> { + System.out.println("Emitting value from MonoSink..."); + sink.success("Hello from MonoSink!"); // Emit the value and complete + scheduler.dispose(); + }, 500, TimeUnit.MILLISECONDS); // Delay for 500 MS + + // Optional: Handle cancellation + sink.onDispose(() -> { + System.out.println("MonoSink disposed (e.g., subscriber cancelled)"); + scheduler.dispose(); + }); + }); + + AwaitSingle awaitSingle = new AwaitSingle(); + delayedMono.subscribe(new TestMonoCoreSubscriber(awaitSingle)); + String result = awaitSingle.await(); + System.out.println("Result of MonoSink is " + result); + + } + + private void printSegments(TraceSegment segment, int indents) { + String segmentName = segment.getName(); + String classname = segment.getClassName(); + String methodName = segment.getMethodName(); + int callCount = segment.getCallCount(); + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < indents; i++) { + sb.append(" "); + } + sb.append("Name: ").append(segmentName); + sb.append(", Class: ").append(classname); + sb.append(", Method: ").append(methodName); + sb.append(", CallCount: ").append(callCount); + Map attributes = segment.getTracerAttributes(); + if(attributes != null) { + Set keys = attributes.keySet(); + for(String key : keys) { + Object value = attributes.get(key); + sb.append(", Attribute: ").append(key); + sb.append(": "); + sb.append(value); + } + } + System.out.println(sb.toString()); + List children = segment.getChildren(); + for(TraceSegment child : children) { + printSegments(child, indents + 2); + } + } + + @Test + public void doFluxPublishOnTest() { + testFluxPub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn4); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn4); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains("Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext")); + TracedMetricData metricData = metrics.get("Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext"); + Assert.assertNotNull(metricData); + Assert.assertEquals(4, metricData.getCallCount()); + + } + + @Test + public void doFluxSubscribeOnTest() { + testFluxSub(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn3); // & txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn3); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + } + + @Test + public void doScheduleTest() { + testScheduler(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + String txnName = introspector.getTransactionNames().iterator().next(); + Map metrics = introspector.getMetricsForTransaction(txnName); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Collection traces = introspector.getTransactionTracesForTransaction(txnName); + TransactionTrace transactionTrace = traces.iterator().next(); + Assert.assertNotNull(transactionTrace); + TraceSegment initial = transactionTrace.getInitialTraceSegment(); + Assert.assertNotNull(initial); + List children = initial.getChildren(); + boolean passes = false; + /* + Ensure that execution is dispatched to another thread and that Mono actions occur on that thread + */ + for(TraceSegment child : children) { + if(child.getName().equals(WRAPPER)) { + List wrapperChildren = child.getChildren(); + Set childNames = new HashSet<>(); + for(TraceSegment wrapperChild : wrapperChildren) { + childNames.add(wrapperChild.getName()); + } + Assert.assertTrue(childNames.contains(MONO_SUBSCRIBE)); + Assert.assertTrue(childNames.contains(MONO_NEXT)); + Assert.assertTrue(childNames.contains(MONO_COMPLETE)); + } + } + } + + + @Trace(dispatcher = true) + public void testScheduler() { + System.out.println("Enter testScheduler"); + Mono mono = getStringMono(); + AwaitSingle await = new AwaitSingle(); + Scheduler scheduler = Schedulers.single(); + scheduler.schedule(() -> { + mono.subscribe(new TestMonoCoreSubscriber(await)); + }); + String result = await.await(); + System.out.println("TestScheduler result: " + result); + + } + + @Trace(dispatcher = true) + public void testMonoPub() { + System.out.println("Enter testMonoPub"); + AwaitSingle await = new AwaitSingle(); + Mono mono = getStringMono(); + + mono.publishOn(Schedulers.single()).subscribe(new TestMonoCoreSubscriber(await)); + await.await(); + + System.out.println("Exit testMonoPub with result: " + await.getResult()); + + } + + @Trace(dispatcher = true) + public void testMonoSub() { + System.out.println("Enter testMonoSub"); + AwaitSingle await = new AwaitSingle(); + + Mono mono = getStringMono().subscribeOn(Schedulers.single()).doOnSubscribe(new SubscriptionConsumer()); + + mono.subscribe(new TestMonoCoreSubscriber(await)); + String result = await.await(); + System.out.println("Exit testMonoSub with result: " + result); + + } + + @Trace(dispatcher = true) + public void testFluxSub() { + System.out.println("Enter testFluxSub"); + + Flux flux = getStringFlux().subscribeOn(Schedulers.single()); + + flux.subscribe(this::doSubscribeAction); + + List list = flux.collectList().block(); + System.out.println("Exit testFluxSub with result: " + list); + } + + @Trace(dispatcher = true) + public void testFluxPub() { + System.out.println("Enter testFluxPub"); + Flux flux = getStringFlux().publishOn(Schedulers.single()); + AwaitMany await = new AwaitMany(); + + flux.subscribe(new TestFluxCoreSubscriber(await,4)); + + List list = await.await(); + System.out.println("Exit testFluxPub with result: "+ list); + + } + + + + public Flux getStringFlux() { + return Flux.fromArray(fluxArray); + } + + public Mono getStringMono() { + + + return Mono.fromCallable(() -> { + try { + Thread.sleep(100L); + } catch(Exception ignored) { + + } + return "hello"; + }); + } + + @Trace + public void doSubscribeAction(String s) { + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Result is "+s); + } + +} diff --git a/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java new file mode 100644 index 0000000000..0d87eb60d0 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java @@ -0,0 +1,52 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; + +import java.util.ArrayList; +import java.util.List; + +public class TestFluxCoreSubscriber implements CoreSubscriber { + + private AwaitMany await = null; + List result = null; + private int numberOfItems = 0; + public TestFluxCoreSubscriber(AwaitMany a, int numberOfItems) { + result = new ArrayList<>(); + await = a; + this.numberOfItems = numberOfItems; + } + + @Trace + public void onNext(String t) { + System.out.println("call to onNext with string: " + t); + result.add(t); + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Object has error: " + t.getMessage()); + if (await != null) { + List result = new ArrayList<>(); + result.add(t.getMessage()); + await.done(result); + } + + } + + @Override + @Trace + public void onComplete() { + await.done(result); + System.out.println("Object has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription s) { + s.request(numberOfItems); + } + +} diff --git a/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java new file mode 100644 index 0000000000..0648030287 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java @@ -0,0 +1,50 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; + +public class TestMonoCoreSubscriber implements CoreSubscriber { + + private AwaitSingle await = null; + String result = null; + + public TestMonoCoreSubscriber(AwaitSingle a) { + await = a; + } + + @Override + @Trace + public void onNext(String t) { + System.out.println("call to onNext with string: " + t); + result = t; + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Object has error: "+t.getMessage()); + if(await != null) { + await.setResult(t.getMessage()); + } + + } + + @Override + @Trace + public void onComplete() { + if(await != null) { + synchronized(await) { + await.setResult(result); + } + } + System.out.println("Object has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription s) { + s.request(1); + } + +} diff --git a/instrumentation/reactor-core-3.3.0/build.gradle b/instrumentation/reactor-core-3.3.0/build.gradle new file mode 100644 index 0000000000..1d8d6a25b7 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/build.gradle @@ -0,0 +1,12 @@ +dependencies { + implementation(project(":agent-bridge")) + implementation("io.projectreactor:reactor-core:3.3.0.RELEASE") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.reactor-core-3.3.0' } +} + +verifyInstrumentation { + passesOnly 'io.projectreactor:reactor-core:[3.3.0.RELEASE,3.4.0)' +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java b/instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java new file mode 100644 index 0000000000..ffb4a47ac8 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java @@ -0,0 +1,35 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; + +public class NRRunnableWrapper implements Runnable { + + private Runnable delegate = null; + + private Token token = null; + private static boolean isTransformed = false; + + public NRRunnableWrapper(Runnable r, Token t) { + delegate = r; + token = t; + if(!isTransformed) { + isTransformed = true; + AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); + } + } + + @Override + @Trace(async=true) + public void run() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + if(delegate != null) { + delegate.run(); + } + } + +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java b/instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java new file mode 100644 index 0000000000..8538825ebe --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java @@ -0,0 +1,25 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; + +public class ReactorUtils { + + public static NRRunnableWrapper getRunnableWrapper(Runnable r) { + if(r instanceof NRRunnableWrapper) {return null;} + + Token currentToken = NewRelic.getAgent().getTransaction().getToken(); + if(currentToken != null) { + if(currentToken.isActive()) { + return new NRRunnableWrapper(r, currentToken); + } else { + currentToken.expire(); + currentToken = null; + return null; + } + } + + return null; + } + +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java new file mode 100644 index 0000000000..42c0db29c5 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -0,0 +1,197 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.CoreSubscriber; + +@Weave(originalName = "reactor.core.publisher.FluxCreate") +class FluxCreate_Instrumentation { + + @Weave(originalName = "reactor.core.publisher.FluxCreate$BaseSink", type = MatchType.BaseClass) + static abstract class BaseSink_Instrumentation implements FluxSink { + + @NewField + protected Token token = null; + + BaseSink_Instrumentation(CoreSubscriber actual) { + if(token == null) { + Token t = NewRelic.getAgent().getTransaction().getToken(); + if(t != null) { + if(!t.isActive()) { + token = t; + } else { + t.expire(); + t = null; + } + } + } + } + + @Trace(async=true) + public void complete() { + + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async=true) + public void error(Throwable e) { + NewRelic.noticeError(e); + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$BufferAsyncSink") + static abstract class BufferAsyncSink_Instrumentation extends BaseSink_Instrumentation { + BufferAsyncSink_Instrumentation(CoreSubscriber actual, int capacityHint) { + super(actual); + } + + @Trace(async=true) + public FluxSink next(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$IgnoreSink") + static abstract class IgnoreSink_Instrumentation extends BaseSink_Instrumentation { + IgnoreSink_Instrumentation(CoreSubscriber actual) { + super(actual); + } + + @Trace(async=true) + public FluxSink next(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$LatestAsyncSink") + static abstract class LatestAsyncSink_Instrumentation extends BaseSink_Instrumentation { + LatestAsyncSink_Instrumentation(CoreSubscriber actual) { + super(actual); + } + + @Trace(async=true) + public FluxSink next(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$NoOverflowBaseAsyncSink", type = MatchType.BaseClass) + static abstract class NoOverflowBaseAsyncSink_Instrumentation extends BaseSink_Instrumentation { + NoOverflowBaseAsyncSink_Instrumentation(CoreSubscriber actual) { + super(actual); + } + + @Trace(async=true) + public FluxSink next(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$SerializeOnRequestSink") + static class SerializeOnRequestSink_Instrumentation { + + @NewField + protected Token token; + + SerializeOnRequestSink_Instrumentation(BaseSink_Instrumentation sink) { + if(sink != null) { + token = sink.token; + } + } + + @Trace(async = true) + public void complete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$SerializedSink") + static class SerializedSink_Instrumentation { + + @NewField + protected Token token; + + SerializedSink_Instrumentation(BaseSink_Instrumentation sink) { + if(sink != null) { + token = sink.token; + } + } + + @Trace(async = true) + public void complete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + + } + +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java new file mode 100644 index 0000000000..b9c91d1b81 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -0,0 +1,69 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.CoreSubscriber; +import reactor.util.annotation.Nullable; + +@Weave(originalName = "reactor.core.publisher.MonoCreate") +class MonoCreate_Instrumentation { + + @Weave(originalName = "reactor.core.publisher.MonoCreate$DefaultMonoSink") + static class DefaultMonoSink_Instrumentation { + + @NewField + private Token token = null; + + DefaultMonoSink_Instrumentation(CoreSubscriber actual) { + Token t = NewRelic.getAgent().getTransaction().getToken(); + if(t != null && t.isActive()) { + token = t; + } else if(t != null) { + t.expire(); + t = null; + } + } + + @Trace(async=true) + public void success() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async=true) + public void success(T value) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async=true) + public void error(Throwable e) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + NewRelic.noticeError(e); + Weaver.callOriginal(); + } + + @Trace(async=true) + public void cancel() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + } + +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java new file mode 100644 index 0000000000..ac922fbc8e --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java @@ -0,0 +1,7 @@ +package reactor.core.scheduler; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +@SkipIfPresent(originalName = "reactor.core.scheduler.ElasticScheduler$DirectScheduleTask") +public class DirectScheduleTask_Skip { +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java new file mode 100644 index 0000000000..7b548210b3 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java @@ -0,0 +1,51 @@ +package reactor.core.scheduler; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.NRRunnableWrapper; +import com.nr.instrumentation.reactor.ReactorUtils; +import reactor.core.Disposable; + +import java.util.concurrent.TimeUnit; + +@Weave(originalName = "reactor.core.scheduler.Scheduler", type = MatchType.Interface) +public class Scheduler_Instrumentation { + + public Disposable schedule(Runnable task) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + public Disposable schedule(Runnable task, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + @Weave(originalName = "reactor.core.scheduler.Scheduler$Worker", type = MatchType.Interface) + public static class Worker_Instrumentation { + + public Disposable schedule(Runnable task) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + public Disposable schedule(Runnable task, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + } +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java new file mode 100644 index 0000000000..2a4b38ae54 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java @@ -0,0 +1,45 @@ +package reactor.core.scheduler; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.NRRunnableWrapper; +import com.nr.instrumentation.reactor.ReactorUtils; + +import reactor.core.Disposable; +import reactor.util.annotation.Nullable; + +@Weave(originalName = "reactor.core.scheduler.Schedulers") +public class Schedulers_Instrumentation { + + @Trace + static Disposable directSchedule(ScheduledExecutorService exec, Runnable task, Disposable parent, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if(wrapper != null) { + task = wrapper; + } + + return Weaver.callOriginal(); + } + + public static Scheduler single() { + return Weaver.callOriginal(); + } + + @Trace + static Disposable workerSchedule(ScheduledExecutorService exec, + Disposable.Composite tasks, + Runnable task, + long delay, + TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if(wrapper != null) { + task = wrapper; + } + + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java new file mode 100644 index 0000000000..394d4e5651 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java @@ -0,0 +1,39 @@ +package com.nr.instrumentation.reactor.test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class AwaitMany { + + private List result = null; + private CompletableFuture> f; + + public AwaitMany() { + f = new CompletableFuture<>(); + } + + public List await() { + List s = Collections.emptyList(); + try { + s = f.get(); + } catch (InterruptedException | ExecutionException ignored) { + } + return s; + } + + public void onError(Throwable t) { + result.add(t.getMessage()); + f.completeExceptionally(t); + } + + public void done(List result) { + System.out.println("AwaitMany done"); + this.result = result; + f.complete(result); + } + +} diff --git a/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java new file mode 100644 index 0000000000..44434be273 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java @@ -0,0 +1,32 @@ +package com.nr.instrumentation.reactor.test; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class AwaitSingle { + + private String result = null; + private CompletableFuture f; + + public AwaitSingle() { + f = new CompletableFuture(); + } + + public String getResult() { + return result; + } + + public String await() { + String s = null; + try { + s = f.get(); + } catch (InterruptedException | ExecutionException ignored) { + } + return s; + } + + public void setResult(String s) { + result = s; + f.complete(result); + } +} diff --git a/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java new file mode 100644 index 0000000000..ceddfe3b71 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java @@ -0,0 +1,35 @@ +package com.nr.instrumentation.reactor.test; + +import org.reactivestreams.Subscription; + +import com.newrelic.api.agent.Trace; + +import reactor.core.CoreSubscriber; + +public class MonoCoreSubscriber implements CoreSubscriber { + + @Override + @Trace + public void onNext(String t) { + System.out.println("Received string for onNext: " + t); + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Received error for onError: " + t); + } + + @Override + @Trace + public void onComplete() { + System.out.println("Mono has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription var1) { + System.out.println("Mono was subscribed to by : " + var1); + } + +} diff --git a/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java new file mode 100644 index 0000000000..b3a2dce717 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java @@ -0,0 +1,23 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; + +import java.util.function.Consumer; + +public class SubscriptionConsumer implements Consumer { + + @Override + @Trace + public void accept(Subscription subscription) { + pause(); + + } + + private void pause() { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + } +} diff --git a/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java new file mode 100644 index 0000000000..c27a6db311 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java @@ -0,0 +1,332 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.agent.introspec.*; +import com.newrelic.api.agent.Trace; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; + +import java.util.*; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = "reactor.core") +public class TestApplication { + + private static final String txn1 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testMonoSub"; + private static final String txn2 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testMonoPub"; + private static final String txn3 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testFluxSub"; + private static final String txn4 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testFluxPub"; + private static final String WRAPPER = "Java/com.nr.instrumentation.reactor.NRRunnableWrapper/run"; + private static final String MONO_NEXT = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onNext"; + private static final String MONO_COMPLETE = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onComplete"; + private static final String MONO_SUBSCRIBE = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onSubscribe"; + private static final String[] fluxArray = {"Message 1", "Message 2", "Message 3", "Message 4"}; + + @Test + public void doMonoSubscribeOnTest() { + testMonoSub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transactions", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn1); // & txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn1); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + Collection traces = introspector.getTransactionTracesForTransaction(txn1); + System.out.println("Transaction traces: " + traces.size()); + for (TransactionTrace transactionTrace : traces) { + TraceSegment initialSegment = transactionTrace.getInitialTraceSegment(); + List children = initialSegment.getChildren(); + boolean passes = false; + /* + Assure that the subscribing occurs on another thread + */ + for (TraceSegment child : children) { + if (child.getName().equals(WRAPPER)) { + Map initialAttributes = initialSegment.getTracerAttributes(); + int initialThread; + if (initialAttributes.containsKey("thread.id")) { + initialThread = Integer.parseInt(initialAttributes.get("thread.id").toString()); + } else { + initialThread = -1; + } + Map attributes = child.getTracerAttributes(); + int childThread; + if (attributes.containsKey("thread.id")) { + childThread = Integer.parseInt(attributes.get("thread.id").toString()); + } else { + childThread = -1; + } + Assert.assertTrue(initialThread != childThread); + passes = true; + break; + } + } + Assert.assertTrue(passes); + + } + } + + @Test + public void doMonoPublishOnTest() { + testMonoPub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn2); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + Collection traces = introspector.getTransactionTracesForTransaction(txn2); + System.out.println("Transaction traces: " + traces.size()); + for(TransactionTrace transactionTrace : traces) { + TraceSegment initialSegment = transactionTrace.getInitialTraceSegment(); + List children = initialSegment.getChildren(); + boolean passes = false; + /* + Assure that the publishing occurs on another thread + */ + for(TraceSegment child : children) { + if(child.getName().equals(WRAPPER)) { + Map initialAttributes = initialSegment.getTracerAttributes(); + int initialThread; + if(initialAttributes.containsKey("thread.id")) { + initialThread = Integer.parseInt(initialAttributes.get("thread.id").toString()); + } else { + initialThread = -1; + } + Map attributes = child.getTracerAttributes(); + int childThread; + if(attributes.containsKey("thread.id")) { + childThread = Integer.parseInt(attributes.get("thread.id").toString()); + } else { + childThread = -1; + } + Assert.assertTrue(initialThread != childThread); + passes = true; + } + } + Assert.assertTrue(passes); + + } + + } + +// private void printSegments(TraceSegment segment, int indents) { +// String segmentName = segment.getName(); +// String classname = segment.getClassName(); +// String methodName = segment.getMethodName(); +// int callCount = segment.getCallCount(); +// StringBuilder sb = new StringBuilder(); +// for(int i = 0; i < indents; i++) { +// sb.append(" "); +// } +// sb.append("Name: ").append(segmentName); +// sb.append(", Class: ").append(classname); +// sb.append(", Method: ").append(methodName); +// sb.append(", CallCount: ").append(callCount); +// Map attributes = segment.getTracerAttributes(); +// if(attributes != null) { +// Set keys = attributes.keySet(); +// for(String key : keys) { +// Object value = attributes.get(key); +// sb.append(", Attribute: ").append(key); +// sb.append(": "); +// sb.append(value); +// } +// } +// System.out.println(sb.toString()); +// List children = segment.getChildren(); +// for(TraceSegment child : children) { +// printSegments(child, indents + 2); +// } +// } + + @Test + public void doFluxPublishOnTest() { + testFluxPub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn4); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn4); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains("Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext")); + TracedMetricData metricData = metrics.get("Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext"); + Assert.assertNotNull(metricData); + Assert.assertEquals(4, metricData.getCallCount()); + + } + + @Test + public void doFluxSubscribeOnTest() { + testFluxSub(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn3); // & txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn3); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + } + + @Test + public void doScheduleTest() { + testScheduler(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + String txnName = introspector.getTransactionNames().iterator().next(); + Map metrics = introspector.getMetricsForTransaction(txnName); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Collection traces = introspector.getTransactionTracesForTransaction(txnName); + TransactionTrace transactionTrace = traces.iterator().next(); + Assert.assertNotNull(transactionTrace); + TraceSegment initial = transactionTrace.getInitialTraceSegment(); + Assert.assertNotNull(initial); + List children = initial.getChildren(); + boolean passes = false; + /* + Ensure that execution is dispatched to another thread and that Mono actions occur on that thread + */ + for(TraceSegment child : children) { + if(child.getName().equals(WRAPPER)) { + List wrapperChildren = child.getChildren(); + Set childNames = new HashSet<>(); + for(TraceSegment wrapperChild : wrapperChildren) { + childNames.add(wrapperChild.getName()); + } + Assert.assertTrue(childNames.contains(MONO_SUBSCRIBE)); + Assert.assertTrue(childNames.contains(MONO_NEXT)); + Assert.assertTrue(childNames.contains(MONO_COMPLETE)); + } + } + } + + + @Trace(dispatcher = true) + public void testScheduler() { + System.out.println("Enter testScheduler"); + Mono mono = getStringMono(); + AwaitSingle await = new AwaitSingle(); + Scheduler scheduler = Schedulers.single(); + scheduler.schedule(() -> { + mono.subscribe(new TestMonoCoreSubscriber(await)); + }); + String result = await.await(); + System.out.println("TestScheduler result: " + result); + + } + + @Trace(dispatcher = true) + public void testMonoPub() { + System.out.println("Enter testMonoPub"); + AwaitSingle await = new AwaitSingle(); + Mono mono = getStringMono(); + + mono.publishOn(Schedulers.single()).subscribe(new TestMonoCoreSubscriber(await)); + await.await(); + + System.out.println("Exit testMonoPub with result: " + await.getResult()); + + } + + @Trace(dispatcher = true) + public void testMonoSub() { + System.out.println("Enter testMonoSub"); + AwaitSingle await = new AwaitSingle(); + + Mono mono = getStringMono().subscribeOn(Schedulers.single()).doOnSubscribe(new SubscriptionConsumer()); + + mono.subscribe(new TestMonoCoreSubscriber(await)); + String result = await.await(); + System.out.println("Exit testMonoSub with result: " + result); + + } + + @Trace(dispatcher = true) + public void testFluxSub() { + System.out.println("Enter testFluxSub"); + + Flux flux = getStringFlux().subscribeOn(Schedulers.single()); + + flux.subscribe(this::doSubscribeAction); + + List list = flux.collectList().block(); + System.out.println("Exit testFluxSub with result: " + list); + } + + @Trace(dispatcher = true) + public void testFluxPub() { + System.out.println("Enter testFluxPub"); + Flux flux = getStringFlux().publishOn(Schedulers.single()); + AwaitMany await = new AwaitMany(); + + flux.subscribe(new TestFluxCoreSubscriber(await,4)); + + List list = await.await(); + System.out.println("Exit testFluxPub with result: "+ list); + + } + + + + public Flux getStringFlux() { + return Flux.fromArray(fluxArray); + } + + public Mono getStringMono() { + + + return Mono.fromCallable(() -> { + try { + Thread.sleep(100L); + } catch(Exception ignored) { + + } + return "hello"; + }); + } + + @Trace + public void doSubscribeAction(String s) { + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Result is "+s); + } + +} diff --git a/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java new file mode 100644 index 0000000000..0d87eb60d0 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java @@ -0,0 +1,52 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; + +import java.util.ArrayList; +import java.util.List; + +public class TestFluxCoreSubscriber implements CoreSubscriber { + + private AwaitMany await = null; + List result = null; + private int numberOfItems = 0; + public TestFluxCoreSubscriber(AwaitMany a, int numberOfItems) { + result = new ArrayList<>(); + await = a; + this.numberOfItems = numberOfItems; + } + + @Trace + public void onNext(String t) { + System.out.println("call to onNext with string: " + t); + result.add(t); + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Object has error: " + t.getMessage()); + if (await != null) { + List result = new ArrayList<>(); + result.add(t.getMessage()); + await.done(result); + } + + } + + @Override + @Trace + public void onComplete() { + await.done(result); + System.out.println("Object has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription s) { + s.request(numberOfItems); + } + +} diff --git a/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java new file mode 100644 index 0000000000..0648030287 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java @@ -0,0 +1,50 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; + +public class TestMonoCoreSubscriber implements CoreSubscriber { + + private AwaitSingle await = null; + String result = null; + + public TestMonoCoreSubscriber(AwaitSingle a) { + await = a; + } + + @Override + @Trace + public void onNext(String t) { + System.out.println("call to onNext with string: " + t); + result = t; + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Object has error: "+t.getMessage()); + if(await != null) { + await.setResult(t.getMessage()); + } + + } + + @Override + @Trace + public void onComplete() { + if(await != null) { + synchronized(await) { + await.setResult(result); + } + } + System.out.println("Object has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription s) { + s.request(1); + } + +} diff --git a/instrumentation/reactor-core-3.4.0/build.gradle b/instrumentation/reactor-core-3.4.0/build.gradle new file mode 100644 index 0000000000..fce386c971 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/build.gradle @@ -0,0 +1,12 @@ +dependencies { + implementation(project(":agent-bridge")) + implementation("io.projectreactor:reactor-core:3.4.0") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.reactor-core-3.4.0' } +} + +verifyInstrumentation { + passesOnly 'io.projectreactor:reactor-core:[3.4.0,3.4.10)' +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java b/instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java new file mode 100644 index 0000000000..ffb4a47ac8 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java @@ -0,0 +1,35 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; + +public class NRRunnableWrapper implements Runnable { + + private Runnable delegate = null; + + private Token token = null; + private static boolean isTransformed = false; + + public NRRunnableWrapper(Runnable r, Token t) { + delegate = r; + token = t; + if(!isTransformed) { + isTransformed = true; + AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); + } + } + + @Override + @Trace(async=true) + public void run() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + if(delegate != null) { + delegate.run(); + } + } + +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java b/instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java new file mode 100644 index 0000000000..8538825ebe --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java @@ -0,0 +1,25 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; + +public class ReactorUtils { + + public static NRRunnableWrapper getRunnableWrapper(Runnable r) { + if(r instanceof NRRunnableWrapper) {return null;} + + Token currentToken = NewRelic.getAgent().getTransaction().getToken(); + if(currentToken != null) { + if(currentToken.isActive()) { + return new NRRunnableWrapper(r, currentToken); + } else { + currentToken.expire(); + currentToken = null; + return null; + } + } + + return null; + } + +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java new file mode 100644 index 0000000000..f38cb6b95f --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -0,0 +1,45 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.EmitterProcessor") +public class EmitterProcessor_Instrumentation { + + @NewField + private Token token; + + EmitterProcessor_Instrumentation(boolean autoCancel, int prefetch) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java new file mode 100644 index 0000000000..687ed717ac --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -0,0 +1,191 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.CoreSubscriber; + +@Weave(originalName = "reactor.core.publisher.FluxCreate") +class FluxCreate_Instrumentation { + + @Weave(originalName = "reactor.core.publisher.FluxCreate$BaseSink", type = MatchType.BaseClass) + static abstract class BaseSink_Instrumentation { + + @NewField + protected Token token; + + BaseSink_Instrumentation(CoreSubscriber actual) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public void complete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable e) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + public void cancel() { + if (token != null) { + token.expire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void request(long n) { + if (token != null) { + token.link(); + } + Weaver.callOriginal(); + } + + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$BufferAsyncSink") + static final class BufferAsyncSink_Instrumentation extends BaseSink_Instrumentation { + + BufferAsyncSink_Instrumentation(CoreSubscriber actual, int capacityHint) { + super(actual); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$IgnoreSink") + static final class IgnoreSink_Instrumentation extends BaseSink_Instrumentation { + + IgnoreSink_Instrumentation(CoreSubscriber actual) { + super(actual); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$NoOverflowBaseAsyncSink", type = MatchType.BaseClass) + static abstract class NoOverflowBaseAsyncSink_Instrumentation extends BaseSink_Instrumentation { + + NoOverflowBaseAsyncSink_Instrumentation(CoreSubscriber actual) { + super(actual); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$SerializedFluxSink") + static final class FluxCreate$SerializedFluxSink_Instrumentation { + + @NewField + private Token token; + + FluxCreate$SerializedFluxSink_Instrumentation(BaseSink_Instrumentation sink) { + if(sink != null) { + if(sink.token != null) { + token = sink.token; + } + } + } + + @Trace(async = true) + public void complete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$SerializeOnRequestSink") + static class SerializeOnRequestSink_Instrumentation { + + @NewField + private Token token; + + SerializeOnRequestSink_Instrumentation(BaseSink_Instrumentation sink) { + if(sink != null) { + if(sink.token != null) { + token = sink.token; + } + } + } + + @Trace(async = true) + public void complete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java new file mode 100644 index 0000000000..2bfddc1fc6 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -0,0 +1,70 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.CoreSubscriber; + +@Weave(originalName = "reactor.core.publisher.MonoCreate") +class MonoCreate_Instrumentation { + + @Weave(originalName = "reactor.core.publisher.MonoCreate$DefaultMonoSink") + static final class DefaultMonoSink_Instrumentation { + + @NewField + private Token token; + + DefaultMonoSink_Instrumentation(CoreSubscriber actual) { + this.token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public void cancel() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable e) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void success() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void success(T value) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void request(long n) { + if(token != null) { + token.link(); + } + Weaver.callOriginal(); + } + + } + +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java new file mode 100644 index 0000000000..15789557d3 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java @@ -0,0 +1,43 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.CorePublisher; + +@Weave(originalName = "reactor.core.publisher.NextProcessor") +class NextProcessor_Instrumentation { + + @NewField + private Token token; + + NextProcessor_Instrumentation(CorePublisher source) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable cause) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitValue(O value) { + token.linkAndExpire(); + token = null; + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitEmpty() { + token.linkAndExpire(); + token = null; + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java new file mode 100644 index 0000000000..28fc5dfbdf --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java @@ -0,0 +1,45 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.ReplayProcessor") +public class ReplayProcessor_Instrumentation { + + @NewField + private Token token; + + ReplayProcessor_Instrumentation(FluxReplay.ReplayBuffer buffer) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java new file mode 100644 index 0000000000..f6fcf43025 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java @@ -0,0 +1,39 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkEmptyMulticast") +class SinkEmptyMulticast_Instrumentation { + + @NewField + protected Token token = null; + + SinkEmptyMulticast_Instrumentation() { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + @Trace(async=true) + public Sinks.EmitResult tryEmitEmpty() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async=true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java new file mode 100644 index 0000000000..b1f7464ae1 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java @@ -0,0 +1,23 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkEmptySerialized", type = MatchType.BaseClass) +class SinkEmptySerialized_Instrumentation { + + @Trace + public Sinks.EmitResult tryEmitEmpty() { + return Weaver.callOriginal(); + } + + @Trace + public Sinks.EmitResult tryEmitError(Throwable t) { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java new file mode 100644 index 0000000000..c8ccb61da7 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java @@ -0,0 +1,46 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkManyBestEffort") +class SinkManyBestEffort_Instrumentation { + + @NewField + private Token token; + + SinkManyBestEffort_Instrumentation(boolean allOrNothing) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java new file mode 100644 index 0000000000..dd90533e2c --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java @@ -0,0 +1,46 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkManySerialized") +class SinkManySerialized_Instrumentation { + + @NewField + private Token token; + + SinkManySerialized_Instrumentation(Sinks.Many sink, ContextHolder contextHolder) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java new file mode 100644 index 0000000000..92d8a5dc50 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java @@ -0,0 +1,14 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkOneSerialized") +public abstract class SinkOneSerialized_Instrumentation extends SinkEmptySerialized_Instrumentation{ + + @Trace + public Sinks.EmitResult tryEmitValue(T t) { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java new file mode 100644 index 0000000000..1454625c3b --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java @@ -0,0 +1,49 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.Disposable; + +import java.util.Queue; +import java.util.function.Consumer; + +@Weave(originalName = "reactor.core.publisher.UnicastManySinkNoBackpressure") +class UnicastManySinkNoBackpressure_Instrumentation { + + @NewField + private Token token; + + UnicastManySinkNoBackpressure_Instrumentation() { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java new file mode 100644 index 0000000000..846579e268 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -0,0 +1,63 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.Disposable; + +import java.util.Queue; +import java.util.function.Consumer; + +@Weave(originalName = "reactor.core.publisher.UnicastProcessor") +public class UnicastProcessor_Instrumentation { + + @NewField + private Token token; + + public UnicastProcessor_Instrumentation(Queue queue) { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + public UnicastProcessor_Instrumentation(Queue queue, Disposable onTerminate) { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + public UnicastProcessor_Instrumentation(Queue queue, Consumer onOverflow, Disposable onTerminate) { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java new file mode 100644 index 0000000000..ac922fbc8e --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java @@ -0,0 +1,7 @@ +package reactor.core.scheduler; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +@SkipIfPresent(originalName = "reactor.core.scheduler.ElasticScheduler$DirectScheduleTask") +public class DirectScheduleTask_Skip { +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java new file mode 100644 index 0000000000..7b548210b3 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java @@ -0,0 +1,51 @@ +package reactor.core.scheduler; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.NRRunnableWrapper; +import com.nr.instrumentation.reactor.ReactorUtils; +import reactor.core.Disposable; + +import java.util.concurrent.TimeUnit; + +@Weave(originalName = "reactor.core.scheduler.Scheduler", type = MatchType.Interface) +public class Scheduler_Instrumentation { + + public Disposable schedule(Runnable task) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + public Disposable schedule(Runnable task, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + @Weave(originalName = "reactor.core.scheduler.Scheduler$Worker", type = MatchType.Interface) + public static class Worker_Instrumentation { + + public Disposable schedule(Runnable task) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + public Disposable schedule(Runnable task, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java new file mode 100644 index 0000000000..2a4b38ae54 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java @@ -0,0 +1,45 @@ +package reactor.core.scheduler; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.NRRunnableWrapper; +import com.nr.instrumentation.reactor.ReactorUtils; + +import reactor.core.Disposable; +import reactor.util.annotation.Nullable; + +@Weave(originalName = "reactor.core.scheduler.Schedulers") +public class Schedulers_Instrumentation { + + @Trace + static Disposable directSchedule(ScheduledExecutorService exec, Runnable task, Disposable parent, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if(wrapper != null) { + task = wrapper; + } + + return Weaver.callOriginal(); + } + + public static Scheduler single() { + return Weaver.callOriginal(); + } + + @Trace + static Disposable workerSchedule(ScheduledExecutorService exec, + Disposable.Composite tasks, + Runnable task, + long delay, + TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if(wrapper != null) { + task = wrapper; + } + + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java new file mode 100644 index 0000000000..3bd1c303c8 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java @@ -0,0 +1,37 @@ +package com.nr.instrumentation.reactor.test; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class AwaitMany { + + private List result = null; + private CompletableFuture> f; + + public AwaitMany() { + f = new CompletableFuture<>(); + } + + public List await() { + List s = Collections.emptyList(); + try { + s = f.get(); + } catch (InterruptedException | ExecutionException ignored) { + } + return s; + } + + public void onError(Throwable t) { + result.add(t.getMessage()); + f.completeExceptionally(t); + } + + public void done(List result) { + System.out.println("AwaitMany done"); + this.result = result; + f.complete(result); + } + +} diff --git a/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java new file mode 100644 index 0000000000..44434be273 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java @@ -0,0 +1,32 @@ +package com.nr.instrumentation.reactor.test; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class AwaitSingle { + + private String result = null; + private CompletableFuture f; + + public AwaitSingle() { + f = new CompletableFuture(); + } + + public String getResult() { + return result; + } + + public String await() { + String s = null; + try { + s = f.get(); + } catch (InterruptedException | ExecutionException ignored) { + } + return s; + } + + public void setResult(String s) { + result = s; + f.complete(result); + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java new file mode 100644 index 0000000000..e2cbd80950 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java @@ -0,0 +1,168 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.agent.introspec.*; +import com.newrelic.api.agent.Trace; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.Sinks; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.*; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = "reactor.core") +public class TestApplication { + + private static final String TRY_EMIT = "Java/reactor.core.publisher.NextProcessor/tryEmitValue"; + private static final String MONO_NEXT = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onNext"; + private static final String MONO_COMPLETE = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onComplete"; + private static final String MONO_SUBSCRIBE = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onSubscribe"; + private static final String BEST_EFFORT_NEXT = "Java/reactor.core.publisher.SinkManyBestEffort/tryEmitNext"; + private static final String SERIALIZED_NEXT = "Java/reactor.core.publisher.SinkManySerialized/tryEmitNext"; + private static final String SUBSCRIBER_NEXT = "Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext"; + private static final String SERIALIZED_COMPLETE = "Java/reactor.core.publisher.SinkManySerialized/tryEmitComplete"; + private static final String BEST_EFFORT_COMPLETE = "Java/reactor.core.publisher.SinkManyBestEffort/tryEmitComplete"; + + @Test + public void doSinkOneTest() { + testSinkOne(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + String txnName = txnNames.iterator().next(); + + Map metrics = introspector.getMetricsForTransaction(txnName); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(TRY_EMIT)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + } + + @Trace(dispatcher = true) + public void testSinkOne() { + + Sinks.One sink = Sinks.one(); + Mono mono = sink.asMono(); + + AwaitSingle awaitSingle = new AwaitSingle(); + mono.subscribe(new TestMonoCoreSubscriber(awaitSingle)); + + Schedulers.single().schedule(() -> { + Sinks.EmitResult result = sink.tryEmitValue("Hello"); + if(result.isFailure()) { + System.out.println("EmitResult threw an error: "+ result); + } + }); + + String result = awaitSingle.await(); + System.out.println("Await Single result : " + result); + } + + @Test + public void doTestSinkMany() { + testSinkMany(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + String txnName = txnNames.iterator().next(); + + Map metrics = introspector.getMetricsForTransaction(txnName); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(BEST_EFFORT_NEXT)); + Assert.assertTrue(names.contains(SERIALIZED_NEXT)); + Assert.assertTrue(names.contains(SUBSCRIBER_NEXT)); + Assert.assertTrue(names.contains(BEST_EFFORT_COMPLETE)); + Assert.assertTrue(names.contains(SERIALIZED_COMPLETE)); + + TracedMetricData metric = metrics.get(BEST_EFFORT_NEXT); + Assert.assertEquals(5, metric.getCallCount()); + + metric = metrics.get(SERIALIZED_NEXT); + Assert.assertEquals(5, metric.getCallCount()); + + metric = metrics.get(SUBSCRIBER_NEXT); + Assert.assertEquals(5, metric.getCallCount()); + + metric = metrics.get(BEST_EFFORT_COMPLETE); + Assert.assertEquals(1, metric.getCallCount()); + + metric = metrics.get(SERIALIZED_COMPLETE); + Assert.assertEquals(1, metric.getCallCount()); + + } + + @Trace(dispatcher = true) + public void testSinkMany() { + Sinks.Many hotSource = Sinks.many().multicast().directBestEffort(); + + Flux flux = hotSource.asFlux(); + AwaitMany awaitMany = new AwaitMany(); + flux.subscribe(new TestFluxCoreSubscriber(awaitMany, 5)); + + Scheduler scheduler = Schedulers.single(); + String[] colors = new String[]{"Red", "Green", "Blue", "Pink", "Purple"}; + for(String color : colors) { + CompletableFuture completableFuture = new CompletableFuture<>(); + scheduler.schedule(() -> { + hotSource.emitNext(color, Sinks.EmitFailureHandler.FAIL_FAST); + completableFuture.complete(true); + }, 100, TimeUnit.MILLISECONDS); + + try { + Boolean result = completableFuture.get(); + } catch (InterruptedException | ExecutionException ignored) { + } + + } + hotSource.emitComplete(Sinks.EmitFailureHandler.FAIL_FAST); + + List results = awaitMany.await(); + System.out.println("Await Many result : " + results); + } + + private void printSegments(TraceSegment segment, int indents) { + String segmentName = segment.getName(); + String classname = segment.getClassName(); + String methodName = segment.getMethodName(); + int callCount = segment.getCallCount(); + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < indents; i++) { + sb.append(" "); + } + sb.append("Name: ").append(segmentName); + sb.append(", Class: ").append(classname); + sb.append(", Method: ").append(methodName); + sb.append(", CallCount: ").append(callCount); + Map attributes = segment.getTracerAttributes(); + if(attributes != null) { + Set keys = attributes.keySet(); + for(String key : keys) { + Object value = attributes.get(key); + sb.append(", Attribute: ").append(key); + sb.append(": "); + sb.append(value); + } + } + System.out.println(sb.toString()); + List children = segment.getChildren(); + for(TraceSegment child : children) { + printSegments(child, indents + 2); + } + } + + +} diff --git a/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java new file mode 100644 index 0000000000..0d87eb60d0 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java @@ -0,0 +1,52 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; + +import java.util.ArrayList; +import java.util.List; + +public class TestFluxCoreSubscriber implements CoreSubscriber { + + private AwaitMany await = null; + List result = null; + private int numberOfItems = 0; + public TestFluxCoreSubscriber(AwaitMany a, int numberOfItems) { + result = new ArrayList<>(); + await = a; + this.numberOfItems = numberOfItems; + } + + @Trace + public void onNext(String t) { + System.out.println("call to onNext with string: " + t); + result.add(t); + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Object has error: " + t.getMessage()); + if (await != null) { + List result = new ArrayList<>(); + result.add(t.getMessage()); + await.done(result); + } + + } + + @Override + @Trace + public void onComplete() { + await.done(result); + System.out.println("Object has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription s) { + s.request(numberOfItems); + } + +} diff --git a/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java new file mode 100644 index 0000000000..0648030287 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java @@ -0,0 +1,50 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; + +public class TestMonoCoreSubscriber implements CoreSubscriber { + + private AwaitSingle await = null; + String result = null; + + public TestMonoCoreSubscriber(AwaitSingle a) { + await = a; + } + + @Override + @Trace + public void onNext(String t) { + System.out.println("call to onNext with string: " + t); + result = t; + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Object has error: "+t.getMessage()); + if(await != null) { + await.setResult(t.getMessage()); + } + + } + + @Override + @Trace + public void onComplete() { + if(await != null) { + synchronized(await) { + await.setResult(result); + } + } + System.out.println("Object has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription s) { + s.request(1); + } + +} diff --git a/instrumentation/reactor-core-3.4.10/build.gradle b/instrumentation/reactor-core-3.4.10/build.gradle new file mode 100644 index 0000000000..6536999932 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/build.gradle @@ -0,0 +1,12 @@ +dependencies { + implementation(project(":agent-bridge")) + implementation("io.projectreactor:reactor-core:3.4.10") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.reactor-core-3.4.10' } +} + +verifyInstrumentation { + passesOnly 'io.projectreactor:reactor-core:[3.4.10,3.5.0)' +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java b/instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java new file mode 100644 index 0000000000..ffb4a47ac8 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java @@ -0,0 +1,35 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; + +public class NRRunnableWrapper implements Runnable { + + private Runnable delegate = null; + + private Token token = null; + private static boolean isTransformed = false; + + public NRRunnableWrapper(Runnable r, Token t) { + delegate = r; + token = t; + if(!isTransformed) { + isTransformed = true; + AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); + } + } + + @Override + @Trace(async=true) + public void run() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + if(delegate != null) { + delegate.run(); + } + } + +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java b/instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java new file mode 100644 index 0000000000..8538825ebe --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java @@ -0,0 +1,25 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; + +public class ReactorUtils { + + public static NRRunnableWrapper getRunnableWrapper(Runnable r) { + if(r instanceof NRRunnableWrapper) {return null;} + + Token currentToken = NewRelic.getAgent().getTransaction().getToken(); + if(currentToken != null) { + if(currentToken.isActive()) { + return new NRRunnableWrapper(r, currentToken); + } else { + currentToken.expire(); + currentToken = null; + return null; + } + } + + return null; + } + +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java new file mode 100644 index 0000000000..f38cb6b95f --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -0,0 +1,45 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.EmitterProcessor") +public class EmitterProcessor_Instrumentation { + + @NewField + private Token token; + + EmitterProcessor_Instrumentation(boolean autoCancel, int prefetch) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java new file mode 100644 index 0000000000..687ed717ac --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -0,0 +1,191 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.CoreSubscriber; + +@Weave(originalName = "reactor.core.publisher.FluxCreate") +class FluxCreate_Instrumentation { + + @Weave(originalName = "reactor.core.publisher.FluxCreate$BaseSink", type = MatchType.BaseClass) + static abstract class BaseSink_Instrumentation { + + @NewField + protected Token token; + + BaseSink_Instrumentation(CoreSubscriber actual) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public void complete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable e) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + public void cancel() { + if (token != null) { + token.expire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void request(long n) { + if (token != null) { + token.link(); + } + Weaver.callOriginal(); + } + + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$BufferAsyncSink") + static final class BufferAsyncSink_Instrumentation extends BaseSink_Instrumentation { + + BufferAsyncSink_Instrumentation(CoreSubscriber actual, int capacityHint) { + super(actual); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$IgnoreSink") + static final class IgnoreSink_Instrumentation extends BaseSink_Instrumentation { + + IgnoreSink_Instrumentation(CoreSubscriber actual) { + super(actual); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$NoOverflowBaseAsyncSink", type = MatchType.BaseClass) + static abstract class NoOverflowBaseAsyncSink_Instrumentation extends BaseSink_Instrumentation { + + NoOverflowBaseAsyncSink_Instrumentation(CoreSubscriber actual) { + super(actual); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$SerializedFluxSink") + static final class FluxCreate$SerializedFluxSink_Instrumentation { + + @NewField + private Token token; + + FluxCreate$SerializedFluxSink_Instrumentation(BaseSink_Instrumentation sink) { + if(sink != null) { + if(sink.token != null) { + token = sink.token; + } + } + } + + @Trace(async = true) + public void complete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$SerializeOnRequestSink") + static class SerializeOnRequestSink_Instrumentation { + + @NewField + private Token token; + + SerializeOnRequestSink_Instrumentation(BaseSink_Instrumentation sink) { + if(sink != null) { + if(sink.token != null) { + token = sink.token; + } + } + } + + @Trace(async = true) + public void complete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java new file mode 100644 index 0000000000..2bfddc1fc6 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -0,0 +1,70 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.CoreSubscriber; + +@Weave(originalName = "reactor.core.publisher.MonoCreate") +class MonoCreate_Instrumentation { + + @Weave(originalName = "reactor.core.publisher.MonoCreate$DefaultMonoSink") + static final class DefaultMonoSink_Instrumentation { + + @NewField + private Token token; + + DefaultMonoSink_Instrumentation(CoreSubscriber actual) { + this.token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public void cancel() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable e) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void success() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void success(T value) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void request(long n) { + if(token != null) { + token.link(); + } + Weaver.callOriginal(); + } + + } + +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java new file mode 100644 index 0000000000..d43d9f4e87 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java @@ -0,0 +1,37 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.CorePublisher; + +@Weave(originalName = "reactor.core.publisher.NextProcessor") +class NextProcessor_Instrumentation { + + @NewField + private Token token; + + NextProcessor_Instrumentation(CorePublisher source) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + Sinks.EmitResult tryEmitError(Throwable cause) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + Sinks.EmitResult tryEmitValue(O value) { + token.linkAndExpire(); + token = null; + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java new file mode 100644 index 0000000000..50e1e42e6f --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java @@ -0,0 +1,45 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.ReplayProcessor") +public class ReplayProcessor_Instrumentation { + + @NewField + private Token token; + + ReplayProcessor_Instrumentation(FluxReplay.ReplayBuffer buffer) { + this.token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java new file mode 100644 index 0000000000..f6fcf43025 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java @@ -0,0 +1,39 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkEmptyMulticast") +class SinkEmptyMulticast_Instrumentation { + + @NewField + protected Token token = null; + + SinkEmptyMulticast_Instrumentation() { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + @Trace(async=true) + public Sinks.EmitResult tryEmitEmpty() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async=true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java new file mode 100644 index 0000000000..b1f7464ae1 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java @@ -0,0 +1,23 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkEmptySerialized", type = MatchType.BaseClass) +class SinkEmptySerialized_Instrumentation { + + @Trace + public Sinks.EmitResult tryEmitEmpty() { + return Weaver.callOriginal(); + } + + @Trace + public Sinks.EmitResult tryEmitError(Throwable t) { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java new file mode 100644 index 0000000000..c8ccb61da7 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java @@ -0,0 +1,46 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkManyBestEffort") +class SinkManyBestEffort_Instrumentation { + + @NewField + private Token token; + + SinkManyBestEffort_Instrumentation(boolean allOrNothing) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java new file mode 100644 index 0000000000..da6bb89e12 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java @@ -0,0 +1,46 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkManySerialized") +class SinkManySerialized_Instrumentation { + + @NewField + private Token token; + + SinkManySerialized_Instrumentation(Sinks.Many sink, ContextHolder contextHolder) { + this.token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java new file mode 100644 index 0000000000..f58b581bb8 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java @@ -0,0 +1,19 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkOneMulticast") +class SinkOneMulticast_Instrumentation extends SinkEmptyMulticast_Instrumentation { + + @Trace(async=true) + public Sinks.EmitResult tryEmitValue(O value) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java new file mode 100644 index 0000000000..92d8a5dc50 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java @@ -0,0 +1,14 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkOneSerialized") +public abstract class SinkOneSerialized_Instrumentation extends SinkEmptySerialized_Instrumentation{ + + @Trace + public Sinks.EmitResult tryEmitValue(T t) { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java new file mode 100644 index 0000000000..1454625c3b --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java @@ -0,0 +1,49 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.Disposable; + +import java.util.Queue; +import java.util.function.Consumer; + +@Weave(originalName = "reactor.core.publisher.UnicastManySinkNoBackpressure") +class UnicastManySinkNoBackpressure_Instrumentation { + + @NewField + private Token token; + + UnicastManySinkNoBackpressure_Instrumentation() { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java new file mode 100644 index 0000000000..846579e268 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -0,0 +1,63 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.Disposable; + +import java.util.Queue; +import java.util.function.Consumer; + +@Weave(originalName = "reactor.core.publisher.UnicastProcessor") +public class UnicastProcessor_Instrumentation { + + @NewField + private Token token; + + public UnicastProcessor_Instrumentation(Queue queue) { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + public UnicastProcessor_Instrumentation(Queue queue, Disposable onTerminate) { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + public UnicastProcessor_Instrumentation(Queue queue, Consumer onOverflow, Disposable onTerminate) { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java new file mode 100644 index 0000000000..ac922fbc8e --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java @@ -0,0 +1,7 @@ +package reactor.core.scheduler; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +@SkipIfPresent(originalName = "reactor.core.scheduler.ElasticScheduler$DirectScheduleTask") +public class DirectScheduleTask_Skip { +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java new file mode 100644 index 0000000000..7b548210b3 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java @@ -0,0 +1,51 @@ +package reactor.core.scheduler; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.NRRunnableWrapper; +import com.nr.instrumentation.reactor.ReactorUtils; +import reactor.core.Disposable; + +import java.util.concurrent.TimeUnit; + +@Weave(originalName = "reactor.core.scheduler.Scheduler", type = MatchType.Interface) +public class Scheduler_Instrumentation { + + public Disposable schedule(Runnable task) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + public Disposable schedule(Runnable task, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + @Weave(originalName = "reactor.core.scheduler.Scheduler$Worker", type = MatchType.Interface) + public static class Worker_Instrumentation { + + public Disposable schedule(Runnable task) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + public Disposable schedule(Runnable task, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java new file mode 100644 index 0000000000..2a4b38ae54 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java @@ -0,0 +1,45 @@ +package reactor.core.scheduler; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.NRRunnableWrapper; +import com.nr.instrumentation.reactor.ReactorUtils; + +import reactor.core.Disposable; +import reactor.util.annotation.Nullable; + +@Weave(originalName = "reactor.core.scheduler.Schedulers") +public class Schedulers_Instrumentation { + + @Trace + static Disposable directSchedule(ScheduledExecutorService exec, Runnable task, Disposable parent, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if(wrapper != null) { + task = wrapper; + } + + return Weaver.callOriginal(); + } + + public static Scheduler single() { + return Weaver.callOriginal(); + } + + @Trace + static Disposable workerSchedule(ScheduledExecutorService exec, + Disposable.Composite tasks, + Runnable task, + long delay, + TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if(wrapper != null) { + task = wrapper; + } + + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.5.0/build.gradle b/instrumentation/reactor-core-3.5.0/build.gradle new file mode 100644 index 0000000000..251a59bef9 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/build.gradle @@ -0,0 +1,12 @@ +dependencies { + implementation(project(":agent-bridge")) + implementation("io.projectreactor:reactor-core:3.5.0") +} + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.reactor-core-3.5.0' } +} + +verifyInstrumentation { + passesOnly 'io.projectreactor:reactor-core:[3.5.0,)' +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java b/instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java new file mode 100644 index 0000000000..ffb4a47ac8 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java @@ -0,0 +1,35 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; + +public class NRRunnableWrapper implements Runnable { + + private Runnable delegate = null; + + private Token token = null; + private static boolean isTransformed = false; + + public NRRunnableWrapper(Runnable r, Token t) { + delegate = r; + token = t; + if(!isTransformed) { + isTransformed = true; + AgentBridge.instrumentation.retransformUninstrumentedClass(getClass()); + } + } + + @Override + @Trace(async=true) + public void run() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + if(delegate != null) { + delegate.run(); + } + } + +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java b/instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java new file mode 100644 index 0000000000..8538825ebe --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/ReactorUtils.java @@ -0,0 +1,25 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; + +public class ReactorUtils { + + public static NRRunnableWrapper getRunnableWrapper(Runnable r) { + if(r instanceof NRRunnableWrapper) {return null;} + + Token currentToken = NewRelic.getAgent().getTransaction().getToken(); + if(currentToken != null) { + if(currentToken.isActive()) { + return new NRRunnableWrapper(r, currentToken); + } else { + currentToken.expire(); + currentToken = null; + return null; + } + } + + return null; + } + +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java new file mode 100644 index 0000000000..f38cb6b95f --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -0,0 +1,45 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.EmitterProcessor") +public class EmitterProcessor_Instrumentation { + + @NewField + private Token token; + + EmitterProcessor_Instrumentation(boolean autoCancel, int prefetch) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java new file mode 100644 index 0000000000..687ed717ac --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -0,0 +1,191 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.CoreSubscriber; + +@Weave(originalName = "reactor.core.publisher.FluxCreate") +class FluxCreate_Instrumentation { + + @Weave(originalName = "reactor.core.publisher.FluxCreate$BaseSink", type = MatchType.BaseClass) + static abstract class BaseSink_Instrumentation { + + @NewField + protected Token token; + + BaseSink_Instrumentation(CoreSubscriber actual) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public void complete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable e) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + public void cancel() { + if (token != null) { + token.expire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void request(long n) { + if (token != null) { + token.link(); + } + Weaver.callOriginal(); + } + + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$BufferAsyncSink") + static final class BufferAsyncSink_Instrumentation extends BaseSink_Instrumentation { + + BufferAsyncSink_Instrumentation(CoreSubscriber actual, int capacityHint) { + super(actual); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$IgnoreSink") + static final class IgnoreSink_Instrumentation extends BaseSink_Instrumentation { + + IgnoreSink_Instrumentation(CoreSubscriber actual) { + super(actual); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$NoOverflowBaseAsyncSink", type = MatchType.BaseClass) + static abstract class NoOverflowBaseAsyncSink_Instrumentation extends BaseSink_Instrumentation { + + NoOverflowBaseAsyncSink_Instrumentation(CoreSubscriber actual) { + super(actual); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$SerializedFluxSink") + static final class FluxCreate$SerializedFluxSink_Instrumentation { + + @NewField + private Token token; + + FluxCreate$SerializedFluxSink_Instrumentation(BaseSink_Instrumentation sink) { + if(sink != null) { + if(sink.token != null) { + token = sink.token; + } + } + } + + @Trace(async = true) + public void complete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } + + @Weave(originalName = "reactor.core.publisher.FluxCreate$SerializeOnRequestSink") + static class SerializeOnRequestSink_Instrumentation { + + @NewField + private Token token; + + SerializeOnRequestSink_Instrumentation(BaseSink_Instrumentation sink) { + if(sink != null) { + if(sink.token != null) { + token = sink.token; + } + } + } + + @Trace(async = true) + public void complete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public FluxSink next(T t) { + if (token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + } +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java new file mode 100644 index 0000000000..13982e5c01 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -0,0 +1,71 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.CoreSubscriber; +import reactor.util.annotation.Nullable; + +@Weave(originalName = "reactor.core.publisher.MonoCreate") +class MonoCreate_Instrumentation { + + @Weave(originalName = "reactor.core.publisher.MonoCreate$DefaultMonoSink") + static final class DefaultMonoSink_Instrumentation { + + @NewField + private Token token; + + DefaultMonoSink_Instrumentation(CoreSubscriber actual) { + this.token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public void cancel() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void error(Throwable e) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void success() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void success(T value) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + @Trace(async = true) + public void request(long n) { + if(token != null) { + token.link(); + } + Weaver.callOriginal(); + } + + } + +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java new file mode 100644 index 0000000000..d43d9f4e87 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java @@ -0,0 +1,37 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.CorePublisher; + +@Weave(originalName = "reactor.core.publisher.NextProcessor") +class NextProcessor_Instrumentation { + + @NewField + private Token token; + + NextProcessor_Instrumentation(CorePublisher source) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + Sinks.EmitResult tryEmitError(Throwable cause) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + Sinks.EmitResult tryEmitValue(O value) { + token.linkAndExpire(); + token = null; + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java new file mode 100644 index 0000000000..50e1e42e6f --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java @@ -0,0 +1,45 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.ReplayProcessor") +public class ReplayProcessor_Instrumentation { + + @NewField + private Token token; + + ReplayProcessor_Instrumentation(FluxReplay.ReplayBuffer buffer) { + this.token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java new file mode 100644 index 0000000000..f6fcf43025 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java @@ -0,0 +1,39 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkEmptyMulticast") +class SinkEmptyMulticast_Instrumentation { + + @NewField + protected Token token = null; + + SinkEmptyMulticast_Instrumentation() { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + @Trace(async=true) + public Sinks.EmitResult tryEmitEmpty() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async=true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java new file mode 100644 index 0000000000..b1f7464ae1 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java @@ -0,0 +1,23 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkEmptySerialized", type = MatchType.BaseClass) +class SinkEmptySerialized_Instrumentation { + + @Trace + public Sinks.EmitResult tryEmitEmpty() { + return Weaver.callOriginal(); + } + + @Trace + public Sinks.EmitResult tryEmitError(Throwable t) { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java new file mode 100644 index 0000000000..c8ccb61da7 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java @@ -0,0 +1,46 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkManyBestEffort") +class SinkManyBestEffort_Instrumentation { + + @NewField + private Token token; + + SinkManyBestEffort_Instrumentation(boolean allOrNothing) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java new file mode 100644 index 0000000000..dd90533e2c --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java @@ -0,0 +1,46 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkManySerialized") +class SinkManySerialized_Instrumentation { + + @NewField + private Token token; + + SinkManySerialized_Instrumentation(Sinks.Many sink, ContextHolder contextHolder) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java new file mode 100644 index 0000000000..f58b581bb8 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java @@ -0,0 +1,19 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkOneMulticast") +class SinkOneMulticast_Instrumentation extends SinkEmptyMulticast_Instrumentation { + + @Trace(async=true) + public Sinks.EmitResult tryEmitValue(O value) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + + } +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java new file mode 100644 index 0000000000..92d8a5dc50 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java @@ -0,0 +1,14 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.publisher.SinkOneSerialized") +public abstract class SinkOneSerialized_Instrumentation extends SinkEmptySerialized_Instrumentation{ + + @Trace + public Sinks.EmitResult tryEmitValue(T t) { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Skip.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Skip.java new file mode 100644 index 0000000000..efbf09c447 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Skip.java @@ -0,0 +1,8 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +@SkipIfPresent(originalName = "reactor.core.publisher.UnicastManySinkNoBackpressure") +class UnicastManySinkNoBackpressure_Skip { + +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java new file mode 100644 index 0000000000..846579e268 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -0,0 +1,63 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.core.Disposable; + +import java.util.Queue; +import java.util.function.Consumer; + +@Weave(originalName = "reactor.core.publisher.UnicastProcessor") +public class UnicastProcessor_Instrumentation { + + @NewField + private Token token; + + public UnicastProcessor_Instrumentation(Queue queue) { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + public UnicastProcessor_Instrumentation(Queue queue, Disposable onTerminate) { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + public UnicastProcessor_Instrumentation(Queue queue, Consumer onOverflow, Disposable onTerminate) { + if(token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitComplete() { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitError(Throwable t) { + if(token != null) { + token.linkAndExpire(); + token = null; + } + return Weaver.callOriginal(); + } + + @Trace(async = true) + public Sinks.EmitResult tryEmitNext(T t) { + if(token != null) { + token.link(); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java new file mode 100644 index 0000000000..ac922fbc8e --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/scheduler/DirectScheduleTask_Skip.java @@ -0,0 +1,7 @@ +package reactor.core.scheduler; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +@SkipIfPresent(originalName = "reactor.core.scheduler.ElasticScheduler$DirectScheduleTask") +public class DirectScheduleTask_Skip { +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java new file mode 100644 index 0000000000..7b548210b3 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/scheduler/Scheduler_Instrumentation.java @@ -0,0 +1,51 @@ +package reactor.core.scheduler; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.NRRunnableWrapper; +import com.nr.instrumentation.reactor.ReactorUtils; +import reactor.core.Disposable; + +import java.util.concurrent.TimeUnit; + +@Weave(originalName = "reactor.core.scheduler.Scheduler", type = MatchType.Interface) +public class Scheduler_Instrumentation { + + public Disposable schedule(Runnable task) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + public Disposable schedule(Runnable task, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + @Weave(originalName = "reactor.core.scheduler.Scheduler$Worker", type = MatchType.Interface) + public static class Worker_Instrumentation { + + public Disposable schedule(Runnable task) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + public Disposable schedule(Runnable task, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if (wrapper != null) { + task = wrapper; + } + return Weaver.callOriginal(); + } + + } +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java new file mode 100644 index 0000000000..2a4b38ae54 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java @@ -0,0 +1,45 @@ +package reactor.core.scheduler; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.NRRunnableWrapper; +import com.nr.instrumentation.reactor.ReactorUtils; + +import reactor.core.Disposable; +import reactor.util.annotation.Nullable; + +@Weave(originalName = "reactor.core.scheduler.Schedulers") +public class Schedulers_Instrumentation { + + @Trace + static Disposable directSchedule(ScheduledExecutorService exec, Runnable task, Disposable parent, long delay, TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if(wrapper != null) { + task = wrapper; + } + + return Weaver.callOriginal(); + } + + public static Scheduler single() { + return Weaver.callOriginal(); + } + + @Trace + static Disposable workerSchedule(ScheduledExecutorService exec, + Disposable.Composite tasks, + Runnable task, + long delay, + TimeUnit unit) { + NRRunnableWrapper wrapper = ReactorUtils.getRunnableWrapper(task); + if(wrapper != null) { + task = wrapper; + } + + return Weaver.callOriginal(); + } +} diff --git a/settings.gradle b/settings.gradle index f8a10508f9..c87b244e48 100644 --- a/settings.gradle +++ b/settings.gradle @@ -352,7 +352,12 @@ include 'instrumentation:r2dbc-postgresql-0.9.2' include 'instrumentation:r2dbc-mssql' include 'instrumentation:rabbit-amqp-2.7.0' include 'instrumentation:rabbit-amqp-5.0.0' -include 'instrumentation:reactor-3.3.0' +//include 'instrumentation:reactor-3.3.0' +include 'instrumentation:reactor-core-3.1.0' +include 'instrumentation:reactor-core-3.3.0' +include 'instrumentation:reactor-core-3.4.0' +include 'instrumentation:reactor-core-3.4.10' +include 'instrumentation:reactor-core-3.5.0' include 'instrumentation:resin-3' include 'instrumentation:resin-4' include 'instrumentation:resin-jmx' From b364c9849c9d279bddd3ac4d7b5c39b81230fff9 Mon Sep 17 00:00:00 2001 From: Doug Hilpipre Date: Thu, 26 Mar 2026 17:03:57 -0500 Subject: [PATCH 2/7] added support for handling errors if configured --- .../instrumentation/NettyReactorConfig.java | 17 --- .../TokenLinkingSubscriber.java | 77 ------------- .../core/publisher/Hooks_Instrumentation.java | 19 ---- .../instrumentation/NettyReactorConfig.java | 18 --- .../reactor/netty/TokenLinkingSubscriber.java | 107 ------------------ .../core/publisher/Hooks_Instrumentation.java | 25 ---- ...antPeriodicWorkerTask_Instrumentation.java | 23 ---- .../PeriodicWorkerTask_Instrumentation.java | 23 ---- .../SchedulerTask_Instrumentation.java | 23 ---- .../scheduler/Schedulers_Instrumentation.java | 45 -------- .../scheduler/WorkerTask_Instrumentation.java | 23 ---- .../http/server/HttpServerMetricsHandler.java | 7 -- .../instrumentation/SubscriptionWrapper.java | 37 ------ .../reactor/netty/TokenLinkingSubscriber.java | 107 ------------------ .../FluxMapFuseable_Instrumentation.java | 68 ----------- .../core/publisher/Hooks_Instrumentation.java | 25 ---- .../LambdaMonoSubscriber_Instrumentation.java | 78 ------------- .../LambdaSubscriber_Instrumentation.java | 77 ------------- .../MonoSubscribeOn_Instrumentation.java | 73 ------------ ...antPeriodicWorkerTask_Instrumentation.java | 23 ---- .../PeriodicWorkerTask_Instrumentation.java | 23 ---- .../SchedulerTask_Instrumentation.java | 23 ---- .../scheduler/Schedulers_Instrumentation.java | 44 ------- .../scheduler/WorkerTask_Instrumentation.java | 23 ---- .../reactor/ReactorConfig.java | 5 +- .../BaseSubscriber_Instrumentation.java | 17 +++ .../DirectProcessor_Instrumentation.java | 17 +++ .../EmitterProcessor_Instrumentation.java | 18 +++ .../EventLoopProcessor_Instrumentation.java | 18 +++ .../publisher/FluxCreate_Instrumentation.java | 2 +- .../publisher/MonoCreate_Instrumentation.java | 2 +- .../MonoProcessor_Instrumentation.java | 18 +++ .../UnicastProcessor_Instrumentation.java | 18 +++ .../reactor/ReactorConfig.java | 14 +++ .../BaseSubscriber_Instrumentation.java | 17 +++ .../DirectProcessor_Instrumentation.java | 17 +++ .../EmitterProcessor_Instrumentation.java | 18 +++ .../EventLoopProcessor_Instrumentation.java | 18 +++ .../MonoProcessor_Instrumentation.java | 18 +++ .../UnicastProcessor_Instrumentation.java | 18 +++ .../reactor/ReactorConfig.java | 14 +++ .../BaseSubscriber_Instrumentation.java | 17 +++ .../DirectProcessor_Instrumentation.java | 17 +++ .../EmitterProcessor_Instrumentation.java | 9 ++ .../NextProcessor_Instrumentation.java | 9 ++ .../UnicastProcessor_Instrumentation.java | 9 ++ .../reactor/ReactorConfig.java | 14 +++ .../BaseSubscriber_Instrumentation.java | 17 +++ .../DirectProcessor_Instrumentation.java | 17 +++ .../EmitterProcessor_Instrumentation.java | 9 ++ .../NextProcessor_Instrumentation.java | 8 ++ .../UnicastProcessor_Instrumentation.java | 9 ++ .../reactor/ReactorConfig.java | 14 +++ .../BaseSubscriber_Instrumentation.java | 17 +++ .../DirectProcessor_Instrumentation.java | 17 +++ .../EmitterProcessor_Instrumentation.java | 9 ++ .../NextProcessor_Instrumentation.java | 8 ++ .../UnicastProcessor_Instrumentation.java | 9 ++ 58 files changed, 455 insertions(+), 1011 deletions(-) delete mode 100644 instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java delete mode 100644 instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/TokenLinkingSubscriber.java delete mode 100644 instrumentation/netty-reactor-0.7.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java delete mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java delete mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpServerMetricsHandler.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/SubscriptionWrapper.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/FluxMapFuseable_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaMonoSubscriber_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaSubscriber_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/MonoSubscribeOn_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/EventLoopProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/EventLoopProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java diff --git a/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java b/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java deleted file mode 100644 index 6a0a3090ee..0000000000 --- a/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package com.nr.instrumentation; - -import com.newrelic.api.agent.NewRelic; - -public class NettyReactorConfig { - public static final boolean errorsEnabled = NewRelic.getAgent().getConfig().getValue("reactor-netty.errors.enabled", false); - - private NettyReactorConfig() { - } -} diff --git a/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/TokenLinkingSubscriber.java b/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/TokenLinkingSubscriber.java deleted file mode 100644 index 3c2d0d5097..0000000000 --- a/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/TokenLinkingSubscriber.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.nr.instrumentation; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; -import reactor.core.CoreSubscriber; -import reactor.core.Fuseable; -import reactor.core.Scannable; -import reactor.core.publisher.Operators; -import reactor.util.context.Context; - -import java.util.function.BiFunction; -import java.util.function.Function; - -// Based on OpenTelemetry code -// https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/TracingSubscriber.java -public class TokenLinkingSubscriber implements CoreSubscriber { - private final Token token; - private final Subscriber subscriber; - private Context context; - - public TokenLinkingSubscriber(Subscriber subscriber, Context ctx) { - this.subscriber = subscriber; - this.context = ctx; - // newrelic-token is added by spring-webflux-5.1 instrumentation of ServerWebExchange - this.token = ctx.getOrDefault("newrelic-token", null); - } - - @Override - public void onSubscribe(Subscription subscription) { - subscriber.onSubscribe(subscription); - } - - @Override - public void onNext(T o) { - withNRToken(() -> subscriber.onNext(o)); - } - - @Override - public void onError(Throwable throwable) { - withNRError(() -> subscriber.onError(throwable), throwable); - } - - @Override - public void onComplete() { - subscriber.onComplete(); - } - - @Override - public Context currentContext() { - return context; - } - - @Trace(async = true, excludeFromTransactionTrace = true) - private void withNRToken(Runnable runnable) { - if (token != null && AgentBridge.getAgent().getTransaction(false) == null) { - token.link(); - } - runnable.run(); - } - - @Trace(async = true, excludeFromTransactionTrace = true) - private void withNRError(Runnable runnable, Throwable throwable) { - if (token != null && token.isActive()) { - token.linkAndExpire(); - if (NettyReactorConfig.errorsEnabled) { - NewRelic.noticeError(throwable); - } - } - runnable.run(); - } - -} \ No newline at end of file diff --git a/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java b/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java deleted file mode 100644 index abe1cd448c..0000000000 --- a/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.publisher; - -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; - -import java.util.concurrent.atomic.AtomicBoolean; - -@Weave(originalName = "reactor.core.publisher.Hooks") -public abstract class Hooks_Instrumentation { - @NewField - public static AtomicBoolean instrumented = new AtomicBoolean(false); -} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java b/instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java deleted file mode 100644 index 07d671a6b2..0000000000 --- a/instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package com.nr.instrumentation; - -import com.newrelic.api.agent.NewRelic; - -public class NettyReactorConfig { - public static final boolean errorsEnabled = NewRelic.getAgent().getConfig() - .getValue("reactor-netty.errors.enabled", false); - - private NettyReactorConfig() { - } -} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java b/instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java deleted file mode 100644 index 541977f380..0000000000 --- a/instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.nr.instrumentation.reactor.netty; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; -import com.nr.instrumentation.NettyReactorConfig; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; -import reactor.core.CoreSubscriber; -import reactor.core.Fuseable; -import reactor.core.Scannable; -import reactor.core.publisher.Operators; -import reactor.util.context.Context; - -import java.util.function.BiFunction; -import java.util.function.Function; - -/** - * Implementation of a reactor.core.CoreSubscriber (a Context aware subscriber) that can be added as - * a lifecycle hook on Flux/Mono operators to propagate, retrieve, and link Tokens across async contexts. - * - * Based on OpenTelemetry code: - * https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/TracingSubscriber.java - * @param - */ -public class TokenLinkingSubscriber implements CoreSubscriber { - private final Token token; - private final Subscriber subscriber; - private Context context; - - public TokenLinkingSubscriber(Subscriber subscriber, Context ctx) { - this.subscriber = subscriber; - this.context = ctx; - // newrelic-token is added by spring-webflux instrumentation of ServerWebExchange - this.token = ctx.getOrDefault("newrelic-token", null); - } - - @Override - public void onSubscribe(Subscription subscription) { - withNRToken(() -> subscriber.onSubscribe(subscription)); - } - - @Override - public void onNext(T o) { - withNRToken(() -> subscriber.onNext(o)); - } - - @Override - public void onError(Throwable throwable) { - withNRError(() -> subscriber.onError(throwable), throwable); - } - - @Override - public void onComplete() { - withNRToken(subscriber::onComplete); - } - - @Override - public Context currentContext() { - return context; - } - - @Trace(async = true, excludeFromTransactionTrace = true) - private void withNRToken(Runnable runnable) { - if (token != null && AgentBridge.getAgent().getTransaction(false) == null) { - token.link(); - } - runnable.run(); - } - - @Trace(async = true, excludeFromTransactionTrace = true) - private void withNRError(Runnable runnable, Throwable throwable) { - if (token != null && token.isActive()) { - token.link(); - if (NettyReactorConfig.errorsEnabled) { - NewRelic.noticeError(throwable); - } - } - runnable.run(); - } - - public static Function, ? extends Publisher> tokenLift() { - return Operators.lift(new TokenLifter<>()); - } - - private static class TokenLifter - implements BiFunction, CoreSubscriber> { - - public TokenLifter() { - } - - @Override - public CoreSubscriber apply(Scannable publisher, CoreSubscriber sub) { - // if Flux/Mono #just, #empty, #error - if (publisher instanceof Fuseable.ScalarCallable) { - return sub; - } - Token token = sub.currentContext().getOrDefault("newrelic-token", null); - if (token != null ) { - return new TokenLinkingSubscriber<>(sub, sub.currentContext()); - } - return sub; - } - } -} \ No newline at end of file diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java deleted file mode 100644 index b573bfefe9..0000000000 --- a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.publisher; - -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; - -import java.util.concurrent.atomic.AtomicBoolean; - -@Weave(originalName = "reactor.core.publisher.Hooks") -public abstract class Hooks_Instrumentation { - - /* - * Note that sub-hooks are cumulative. We want to avoid setting the same sub-hooks - * more than once, so we set this boolean to true the first time we set a sub-hook. - * if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { Hooks.onEachOperator(...) } - */ - @NewField - public static AtomicBoolean instrumented = new AtomicBoolean(false); -} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java deleted file mode 100644 index 4095149fa9..0000000000 --- a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(originalName = "reactor.core.scheduler.InstantPeriodicWorkerTask") -final class InstantPeriodicWorkerTask_Instrumentation { - - // We need to be able to link the Token here when executing on a supplied Scheduler - // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator - @Trace(async = true, excludeFromTransactionTrace = true) - public Void call() { - return Weaver.callOriginal(); - } -} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java deleted file mode 100644 index 988d33ce26..0000000000 --- a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(originalName = "reactor.core.scheduler.PeriodicWorkerTask") -final class PeriodicWorkerTask_Instrumentation { - - // We need to be able to link the Token here when executing on a supplied Scheduler - // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator - @Trace(async = true, excludeFromTransactionTrace = true) - public Void call() { - return Weaver.callOriginal(); - } -} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java deleted file mode 100644 index fda06f032a..0000000000 --- a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(originalName = "reactor.core.scheduler.SchedulerTask") -final class SchedulerTask_Instrumentation { - - // We need to be able to link the Token here when executing on a supplied Scheduler via Mono::publishOn - // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator - @Trace(async = true, excludeFromTransactionTrace = true) - public Void call() { - return Weaver.callOriginal(); - } -} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java deleted file mode 100644 index 29ae1e901a..0000000000 --- a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2021 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber; -import reactor.core.publisher.Hooks; -import reactor.core.publisher.Hooks_Instrumentation; - -import static com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber.tokenLift; - -@Weave(type = MatchType.BaseClass, originalName = "reactor.core.scheduler.Schedulers") -public abstract class Schedulers_Instrumentation { - - @Weave(type = MatchType.ExactClass, originalName = "reactor.core.scheduler.Schedulers$CachedScheduler") - static class CachedScheduler { - final Scheduler cached; - final String key; - - CachedScheduler(String key, Scheduler cached) { - /* - * Add tokenLift hook if it hasn't already been added. This allows for tokens to be retrieved from - * the current context and linked across threads at various points of the Flux/Mono lifecycle. - * - * When using Netty Reactor with SpringBoot this hook will be added by the HttpTrafficHandler_Instrumentation - * but when using other embedded web servers (e.g. Tomcat, Jetty, Undertow) the HttpTrafficHandler class - * doesn't get loaded and thus the hook isn't added. This ensures that the hook is added in a common code - * path before any Scheduler Tasks are spun off on new threads. - */ - if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { - Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); - } - - this.cached = Weaver.callOriginal(); - this.key = Weaver.callOriginal(); - } - - } - -} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java deleted file mode 100644 index 76867aca47..0000000000 --- a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(originalName = "reactor.core.scheduler.WorkerTask") -final class WorkerTask_Instrumentation { - - // We need to be able to link the Token here when executing on a supplied Scheduler - // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator - @Trace(async = true, excludeFromTransactionTrace = true) - public Void call() { - return Weaver.callOriginal(); - } -} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpServerMetricsHandler.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpServerMetricsHandler.java deleted file mode 100644 index 65a36cd64b..0000000000 --- a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpServerMetricsHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package reactor.netty.http.server; - -import com.newrelic.api.agent.weaver.SkipIfPresent; - -@SkipIfPresent -final class HttpServerMetricsHandler { -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/SubscriptionWrapper.java b/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/SubscriptionWrapper.java deleted file mode 100644 index 4d011a81f7..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/SubscriptionWrapper.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * * Copyright 2026 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package com.nr.instrumentation; - -import com.newrelic.api.agent.Token; -import org.reactivestreams.Subscription; -import reactor.util.context.Context; - -public class SubscriptionWrapper implements Subscription { - - Subscription delegate; - Context currentContext; - - public SubscriptionWrapper(Subscription delegate, Context context) { - this.delegate = delegate; - this.currentContext = context; - } - - @Override - public void request(long n) { - delegate.request(n); - } - - @Override - public void cancel() { - Token token = currentContext.getOrDefault("newrelic-token", null); - if (token != null) { - token.linkAndExpire(); - } - if (delegate != null) delegate.cancel(); - } -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java b/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java deleted file mode 100644 index 541977f380..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.nr.instrumentation.reactor.netty; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; -import com.nr.instrumentation.NettyReactorConfig; -import org.reactivestreams.Publisher; -import org.reactivestreams.Subscriber; -import org.reactivestreams.Subscription; -import reactor.core.CoreSubscriber; -import reactor.core.Fuseable; -import reactor.core.Scannable; -import reactor.core.publisher.Operators; -import reactor.util.context.Context; - -import java.util.function.BiFunction; -import java.util.function.Function; - -/** - * Implementation of a reactor.core.CoreSubscriber (a Context aware subscriber) that can be added as - * a lifecycle hook on Flux/Mono operators to propagate, retrieve, and link Tokens across async contexts. - * - * Based on OpenTelemetry code: - * https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/TracingSubscriber.java - * @param - */ -public class TokenLinkingSubscriber implements CoreSubscriber { - private final Token token; - private final Subscriber subscriber; - private Context context; - - public TokenLinkingSubscriber(Subscriber subscriber, Context ctx) { - this.subscriber = subscriber; - this.context = ctx; - // newrelic-token is added by spring-webflux instrumentation of ServerWebExchange - this.token = ctx.getOrDefault("newrelic-token", null); - } - - @Override - public void onSubscribe(Subscription subscription) { - withNRToken(() -> subscriber.onSubscribe(subscription)); - } - - @Override - public void onNext(T o) { - withNRToken(() -> subscriber.onNext(o)); - } - - @Override - public void onError(Throwable throwable) { - withNRError(() -> subscriber.onError(throwable), throwable); - } - - @Override - public void onComplete() { - withNRToken(subscriber::onComplete); - } - - @Override - public Context currentContext() { - return context; - } - - @Trace(async = true, excludeFromTransactionTrace = true) - private void withNRToken(Runnable runnable) { - if (token != null && AgentBridge.getAgent().getTransaction(false) == null) { - token.link(); - } - runnable.run(); - } - - @Trace(async = true, excludeFromTransactionTrace = true) - private void withNRError(Runnable runnable, Throwable throwable) { - if (token != null && token.isActive()) { - token.link(); - if (NettyReactorConfig.errorsEnabled) { - NewRelic.noticeError(throwable); - } - } - runnable.run(); - } - - public static Function, ? extends Publisher> tokenLift() { - return Operators.lift(new TokenLifter<>()); - } - - private static class TokenLifter - implements BiFunction, CoreSubscriber> { - - public TokenLifter() { - } - - @Override - public CoreSubscriber apply(Scannable publisher, CoreSubscriber sub) { - // if Flux/Mono #just, #empty, #error - if (publisher instanceof Fuseable.ScalarCallable) { - return sub; - } - Token token = sub.currentContext().getOrDefault("newrelic-token", null); - if (token != null ) { - return new TokenLinkingSubscriber<>(sub, sub.currentContext()); - } - return sub; - } - } -} \ No newline at end of file diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/FluxMapFuseable_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/FluxMapFuseable_Instrumentation.java deleted file mode 100644 index b16534d7c9..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/FluxMapFuseable_Instrumentation.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * * Copyright 2024 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.publisher; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.WeaveAllConstructors; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(type = MatchType.ExactClass, originalName = "reactor.core.publisher.FluxMapFuseable") -abstract class FluxMapFuseable_Instrumentation { - - @Weave(type = MatchType.ExactClass, originalName = "reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber") - static final class MapFuseableSubscriber_Instrumentation { - @NewField - private Token token; - - @WeaveAllConstructors - MapFuseableSubscriber_Instrumentation() { - if (AgentBridge.getAgent().getTransaction(false) != null && token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - public void onComplete() { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - - public void onNext(T t) { - if (token != null) { - // not ideal to do this in onNext, but we're seeing situations where nothing else is ever called - // no onComplete, no onError, no cancel - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - - public void onError(Throwable t) { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - - public void cancel() { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - } -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java deleted file mode 100644 index b573bfefe9..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.publisher; - -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; - -import java.util.concurrent.atomic.AtomicBoolean; - -@Weave(originalName = "reactor.core.publisher.Hooks") -public abstract class Hooks_Instrumentation { - - /* - * Note that sub-hooks are cumulative. We want to avoid setting the same sub-hooks - * more than once, so we set this boolean to true the first time we set a sub-hook. - * if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { Hooks.onEachOperator(...) } - */ - @NewField - public static AtomicBoolean instrumented = new AtomicBoolean(false); -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaMonoSubscriber_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaMonoSubscriber_Instrumentation.java deleted file mode 100644 index 23e90a78f7..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaMonoSubscriber_Instrumentation.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.publisher; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.WeaveAllConstructors; -import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.SubscriptionWrapper; -import org.reactivestreams.Subscription; -import reactor.util.context.Context; - -@Weave(originalName = "reactor.core.publisher.LambdaMonoSubscriber") -abstract class LambdaMonoSubscriber_Instrumentation { - @NewField - private Context nrContext; - final Context initialContext = Weaver.callOriginal(); - - @WeaveAllConstructors - protected LambdaMonoSubscriber_Instrumentation() { - // LamdaMonoSubscriber creates a new Context, so we create a new token and put it on the Context - // to be linked by TokenLinkingSubscriber but expired on onComplete here - if (AgentBridge.getAgent().getTransaction(false) != null - && initialContext.getOrDefault("newrelic-token", null) == null) { - nrContext = Context.of("newrelic-token", NewRelic.getAgent().getTransaction().getToken()); - } - } - - public final void onComplete() { - Token token = this.currentContext().getOrDefault("newrelic-token", null); - if (token != null) { - token.expire(); - this.nrContext = null; - } - Weaver.callOriginal(); - } - - public final void onError(Throwable t) { - Token token = this.currentContext().getOrDefault("newrelic-token", null); - if (token != null) { - token.expire(); - this.nrContext = null; - } - Weaver.callOriginal(); - } - - public void dispose() { - Token token = this.currentContext().getOrDefault("newrelic-token", null); - if (token != null) { - token.expire(); - this.nrContext = null; - } - Weaver.callOriginal(); - } - - public final void onSubscribe(Subscription s) { - if (s != null) { - s = new SubscriptionWrapper(s, this.currentContext()); - } - Weaver.callOriginal(); - } - - public Context currentContext() { - if (nrContext != null) { - return initialContext.putAll(nrContext); - } - return Weaver.callOriginal(); - } - -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaSubscriber_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaSubscriber_Instrumentation.java deleted file mode 100644 index 2708bbf9b2..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaSubscriber_Instrumentation.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.publisher; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.WeaveAllConstructors; -import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.SubscriptionWrapper; -import org.reactivestreams.Subscription; -import reactor.util.context.Context; - -@Weave(originalName = "reactor.core.publisher.LambdaSubscriber") -abstract class LambdaSubscriber_Instrumentation { - final Context initialContext = Weaver.callOriginal(); - @NewField - private Context nrContext; - - @WeaveAllConstructors - protected LambdaSubscriber_Instrumentation() { - if (AgentBridge.getAgent().getTransaction(false) != null - && initialContext.getOrDefault("newrelic-token", null) == null) { - nrContext = Context.of("newrelic-token", NewRelic.getAgent().getTransaction().getToken()); - } - } - - public final void onComplete() { - Token token = this.currentContext().getOrDefault("newrelic-token", null); - if (token != null) { - token.expire(); - this.nrContext = null; - } - Weaver.callOriginal(); - } - - public final void onError(Throwable t) { - Token token = this.currentContext().getOrDefault("newrelic-token", null); - if (token != null) { - token.expire(); - this.nrContext = null; - } - Weaver.callOriginal(); - } - - public void dispose() { - Token token = this.currentContext().getOrDefault("newrelic-token", null); - if (token != null) { - token.expire(); - this.nrContext = null; - } - Weaver.callOriginal(); - } - - public final void onSubscribe(Subscription s) { - if (s != null) { - s = new SubscriptionWrapper(s, this.currentContext()); - } - Weaver.callOriginal(); - } - - public Context currentContext() { - if (nrContext != null) { - //return nrContext; - return initialContext.putAll(nrContext); - } - return Weaver.callOriginal(); - } - -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/MonoSubscribeOn_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/MonoSubscribeOn_Instrumentation.java deleted file mode 100644 index 40695bd9c6..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/MonoSubscribeOn_Instrumentation.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * - * * Copyright 2024 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.publisher; - -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.NewField; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.WeaveAllConstructors; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(type = MatchType.ExactClass, originalName = "reactor.core.publisher.MonoSubscribeOn") -abstract class MonoSubscribeOn_Instrumentation { - - @Weave(type = MatchType.ExactClass, originalName = "reactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber") - static final class SubscribeOnSubscriber_Instrumentation { - @NewField - private Token token; - - @WeaveAllConstructors - SubscribeOnSubscriber_Instrumentation() { - if (NewRelic.getAgent().getTransaction() != null && token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - public void run () { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - - public void onNext(T t) { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - - public void onComplete() { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - - public void onError(Throwable t) { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - - public void cancel () { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Weaver.callOriginal(); - } - } -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java deleted file mode 100644 index 4095149fa9..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(originalName = "reactor.core.scheduler.InstantPeriodicWorkerTask") -final class InstantPeriodicWorkerTask_Instrumentation { - - // We need to be able to link the Token here when executing on a supplied Scheduler - // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator - @Trace(async = true, excludeFromTransactionTrace = true) - public Void call() { - return Weaver.callOriginal(); - } -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java deleted file mode 100644 index 988d33ce26..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(originalName = "reactor.core.scheduler.PeriodicWorkerTask") -final class PeriodicWorkerTask_Instrumentation { - - // We need to be able to link the Token here when executing on a supplied Scheduler - // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator - @Trace(async = true, excludeFromTransactionTrace = true) - public Void call() { - return Weaver.callOriginal(); - } -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java deleted file mode 100644 index fda06f032a..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(originalName = "reactor.core.scheduler.SchedulerTask") -final class SchedulerTask_Instrumentation { - - // We need to be able to link the Token here when executing on a supplied Scheduler via Mono::publishOn - // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator - @Trace(async = true, excludeFromTransactionTrace = true) - public Void call() { - return Weaver.callOriginal(); - } -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java deleted file mode 100644 index 9bef1d7d91..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2021 New Relic Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber; -import reactor.core.publisher.Hooks; -import reactor.core.publisher.Hooks_Instrumentation; - -import static com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber.tokenLift; - -@Weave(type = MatchType.BaseClass, originalName = "reactor.core.scheduler.Schedulers") -public abstract class Schedulers_Instrumentation { - - @Weave(type = MatchType.ExactClass, originalName = "reactor.core.scheduler.Schedulers$CachedScheduler") - static class CachedScheduler { - final Scheduler cached; - final String stringRepresentation; - - CachedScheduler(String key, Scheduler cached) { - /* - * Add tokenLift hook if it hasn't already been added. This allows for tokens to be retrieved from - * the current context and linked across threads at various points of the Flux/Mono lifecycle. - * - * When using Netty Reactor with SpringBoot this hook will be added by the HttpTrafficHandler_Instrumentation - * but when using other embedded web servers (e.g. Tomcat, Jetty, Undertow) the HttpTrafficHandler class - * doesn't get loaded and thus the hook isn't added. This ensures that the hook is added in a common code - * path before any Scheduler Tasks are spun off on new threads. - */ - if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { - Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); - } - - this.cached = Weaver.callOriginal(); - this.stringRepresentation = Weaver.callOriginal(); - } - } - -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java deleted file mode 100644 index 76867aca47..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.core.scheduler; - -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; - -@Weave(originalName = "reactor.core.scheduler.WorkerTask") -final class WorkerTask_Instrumentation { - - // We need to be able to link the Token here when executing on a supplied Scheduler - // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator - @Trace(async = true, excludeFromTransactionTrace = true) - public Void call() { - return Weaver.callOriginal(); - } -} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java b/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java index 377beab791..ebca10190e 100644 --- a/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java +++ b/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java @@ -4,6 +4,9 @@ public class ReactorConfig { public static final boolean errorsEnabledNetty = NewRelic.getAgent().getConfig().getValue("reactor-netty.errors.enabled", false); - public static final boolean errorsEnabled = NewRelic.getAgent().getConfig().getValue("reactor.errors.enabled", false); + public static final boolean errorsRectorEnabled = NewRelic.getAgent().getConfig().getValue("reactor.errors.enabled", false); + public static final boolean errorsEnabled = errorsRectorEnabled || errorsEnabledNetty; + private ReactorConfig() { + } } diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java new file mode 100644 index 0000000000..b6c1d1f337 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java @@ -0,0 +1,17 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.BaseSubscriber") +public class BaseSubscriber_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java new file mode 100644 index 0000000000..729236d716 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java @@ -0,0 +1,17 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.DirectProcessor") +public class DirectProcessor_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java new file mode 100644 index 0000000000..df30580ee9 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -0,0 +1,18 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.EmitterProcessor") +public class EmitterProcessor_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/EventLoopProcessor_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/EventLoopProcessor_Instrumentation.java new file mode 100644 index 0000000000..e3d4644c38 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/EventLoopProcessor_Instrumentation.java @@ -0,0 +1,18 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.EventLoopProcessor") +class EventLoopProcessor_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java index ee36a8e34a..e763d2a24f 100644 --- a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -44,7 +44,7 @@ public void complete() { @Trace(async=true) public void error(Throwable e) { - if(ReactorConfig.errorsEnabled || ReactorConfig.errorsEnabledNetty) { + if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(e); } if (token != null) { diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java index b38128da99..325d953616 100644 --- a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -48,7 +48,7 @@ public void success(T value) { @Trace(async=true) public void error(Throwable e) { - if(ReactorConfig.errorsEnabled || ReactorConfig.errorsEnabledNetty) { + if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(e); } if(token != null) { diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoProcessor_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoProcessor_Instrumentation.java new file mode 100644 index 0000000000..cde9ad26a2 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoProcessor_Instrumentation.java @@ -0,0 +1,18 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.MonoProcessor") +public class MonoProcessor_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java new file mode 100644 index 0000000000..2eebc2c253 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -0,0 +1,18 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.UnicastProcessor") +public class UnicastProcessor_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java b/instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java new file mode 100644 index 0000000000..d90133121f --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java @@ -0,0 +1,14 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.api.agent.NewRelic; + +public class ReactorConfig { + + public static final boolean errorsEnabledNetty = NewRelic.getAgent().getConfig().getValue("reactor-netty.errors.enabled", false); + public static final boolean errorsRectorEnabled = NewRelic.getAgent().getConfig().getValue("reactor.errors.enabled", false); + public static final boolean errorsEnabled = errorsRectorEnabled || errorsEnabledNetty; + + private ReactorConfig() { + } + +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java new file mode 100644 index 0000000000..b6c1d1f337 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java @@ -0,0 +1,17 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.BaseSubscriber") +public class BaseSubscriber_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java new file mode 100644 index 0000000000..729236d716 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java @@ -0,0 +1,17 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.DirectProcessor") +public class DirectProcessor_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java new file mode 100644 index 0000000000..df30580ee9 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -0,0 +1,18 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.EmitterProcessor") +public class EmitterProcessor_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/EventLoopProcessor_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/EventLoopProcessor_Instrumentation.java new file mode 100644 index 0000000000..e3d4644c38 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/EventLoopProcessor_Instrumentation.java @@ -0,0 +1,18 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.EventLoopProcessor") +class EventLoopProcessor_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoProcessor_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoProcessor_Instrumentation.java new file mode 100644 index 0000000000..cde9ad26a2 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoProcessor_Instrumentation.java @@ -0,0 +1,18 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.MonoProcessor") +public class MonoProcessor_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java new file mode 100644 index 0000000000..2eebc2c253 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -0,0 +1,18 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.UnicastProcessor") +public class UnicastProcessor_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java b/instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java new file mode 100644 index 0000000000..d90133121f --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java @@ -0,0 +1,14 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.api.agent.NewRelic; + +public class ReactorConfig { + + public static final boolean errorsEnabledNetty = NewRelic.getAgent().getConfig().getValue("reactor-netty.errors.enabled", false); + public static final boolean errorsRectorEnabled = NewRelic.getAgent().getConfig().getValue("reactor.errors.enabled", false); + public static final boolean errorsEnabled = errorsRectorEnabled || errorsEnabledNetty; + + private ReactorConfig() { + } + +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java new file mode 100644 index 0000000000..b6c1d1f337 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java @@ -0,0 +1,17 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.BaseSubscriber") +public class BaseSubscriber_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java new file mode 100644 index 0000000000..729236d716 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java @@ -0,0 +1,17 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.DirectProcessor") +public class DirectProcessor_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java index f38cb6b95f..2d72a90ba3 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.EmitterProcessor") public class EmitterProcessor_Instrumentation { @@ -42,4 +43,12 @@ public Sinks.EmitResult tryEmitNext(T t) { } return Weaver.callOriginal(); } + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java index 15789557d3..2f04090f0b 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.CorePublisher; @Weave(originalName = "reactor.core.publisher.NextProcessor") @@ -40,4 +41,12 @@ public Sinks.EmitResult tryEmitEmpty() { token = null; return Weaver.callOriginal(); } + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java index 846579e268..31bfdea1a3 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.Disposable; import java.util.Queue; @@ -60,4 +61,12 @@ public Sinks.EmitResult tryEmitNext(T t) { } return Weaver.callOriginal(); } + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java b/instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java new file mode 100644 index 0000000000..d90133121f --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java @@ -0,0 +1,14 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.api.agent.NewRelic; + +public class ReactorConfig { + + public static final boolean errorsEnabledNetty = NewRelic.getAgent().getConfig().getValue("reactor-netty.errors.enabled", false); + public static final boolean errorsRectorEnabled = NewRelic.getAgent().getConfig().getValue("reactor.errors.enabled", false); + public static final boolean errorsEnabled = errorsRectorEnabled || errorsEnabledNetty; + + private ReactorConfig() { + } + +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java new file mode 100644 index 0000000000..b6c1d1f337 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java @@ -0,0 +1,17 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.BaseSubscriber") +public class BaseSubscriber_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java new file mode 100644 index 0000000000..729236d716 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java @@ -0,0 +1,17 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.DirectProcessor") +public class DirectProcessor_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java index f38cb6b95f..2d72a90ba3 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.EmitterProcessor") public class EmitterProcessor_Instrumentation { @@ -42,4 +43,12 @@ public Sinks.EmitResult tryEmitNext(T t) { } return Weaver.callOriginal(); } + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java index d43d9f4e87..62b35e2f4d 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.CorePublisher; @Weave(originalName = "reactor.core.publisher.NextProcessor") @@ -34,4 +35,11 @@ Sinks.EmitResult tryEmitValue(O value) { return Weaver.callOriginal(); } + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java index 846579e268..31bfdea1a3 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.Disposable; import java.util.Queue; @@ -60,4 +61,12 @@ public Sinks.EmitResult tryEmitNext(T t) { } return Weaver.callOriginal(); } + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java b/instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java new file mode 100644 index 0000000000..d90133121f --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/ReactorConfig.java @@ -0,0 +1,14 @@ +package com.nr.instrumentation.reactor; + +import com.newrelic.api.agent.NewRelic; + +public class ReactorConfig { + + public static final boolean errorsEnabledNetty = NewRelic.getAgent().getConfig().getValue("reactor-netty.errors.enabled", false); + public static final boolean errorsRectorEnabled = NewRelic.getAgent().getConfig().getValue("reactor.errors.enabled", false); + public static final boolean errorsEnabled = errorsRectorEnabled || errorsEnabledNetty; + + private ReactorConfig() { + } + +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java new file mode 100644 index 0000000000..b6c1d1f337 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/BaseSubscriber_Instrumentation.java @@ -0,0 +1,17 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.BaseSubscriber") +public class BaseSubscriber_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java new file mode 100644 index 0000000000..729236d716 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/DirectProcessor_Instrumentation.java @@ -0,0 +1,17 @@ +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; + +@Weave(originalName = "reactor.core.publisher.DirectProcessor") +public class DirectProcessor_Instrumentation { + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java index f38cb6b95f..2d72a90ba3 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.EmitterProcessor") public class EmitterProcessor_Instrumentation { @@ -42,4 +43,12 @@ public Sinks.EmitResult tryEmitNext(T t) { } return Weaver.callOriginal(); } + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java index d43d9f4e87..62b35e2f4d 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.CorePublisher; @Weave(originalName = "reactor.core.publisher.NextProcessor") @@ -34,4 +35,11 @@ Sinks.EmitResult tryEmitValue(O value) { return Weaver.callOriginal(); } + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java index 846579e268..31bfdea1a3 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.Disposable; import java.util.Queue; @@ -60,4 +61,12 @@ public Sinks.EmitResult tryEmitNext(T t) { } return Weaver.callOriginal(); } + + public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } + Weaver.callOriginal(); + } + } From 85711b1ad6d95d0040639b20448af7929f27fd8c Mon Sep 17 00:00:00 2001 From: Doug Hilpipre Date: Thu, 2 Apr 2026 08:54:39 -0500 Subject: [PATCH 3/7] changes to replace existing reactor instrumentation --- .../netty-reactor-0.8.0/NOTICE.txt | 17 - instrumentation/netty-reactor-0.8.0/README.md | 36 -- .../netty-reactor-0.8.0/build.gradle | 19 - .../HttpClientConnect_Instrumentation.java | 36 -- .../HttpTrafficHandler_Instrumentation.java | 35 -- .../TransactionPropagationTest.java | 246 ------------ .../netty-reactor-0.9.0/NOTICE.txt | 17 - instrumentation/netty-reactor-0.9.0/README.md | 36 -- .../netty-reactor-0.9.0/build.gradle | 19 - .../instrumentation/NettyReactorConfig.java | 18 - .../HttpClientConnect_Instrumentation.java | 36 -- .../HttpTrafficHandler_Instrumentation.java | 35 -- .../TransactionPropagationTest.java | 351 ------------------ .../publisher/MonoCreate_Instrumentation.java | 1 - .../publisher/FluxCreate_Instrumentation.java | 5 +- .../publisher/MonoCreate_Instrumentation.java | 5 +- .../publisher/FluxCreate_Instrumentation.java | 4 + .../publisher/MonoCreate_Instrumentation.java | 4 + .../ReplayProcessor_Instrumentation.java | 4 + .../SinkEmptyMulticast_Instrumentation.java | 4 + .../SinkEmptySerialized_Instrumentation.java | 4 + .../SinkManyBestEffort_Instrumentation.java | 4 + .../SinkManySerialized_Instrumentation.java | 4 + ...anySinkNoBackpressure_Instrumentation.java | 4 + .../UnicastProcessor_Instrumentation.java | 3 + .../EmitterProcessor_Instrumentation.java | 3 + .../publisher/FluxCreate_Instrumentation.java | 6 +- .../publisher/MonoCreate_Instrumentation.java | 4 + .../NextProcessor_Instrumentation.java | 3 + .../ReplayProcessor_Instrumentation.java | 4 + .../SinkEmptyMulticast_Instrumentation.java | 4 + .../SinkEmptySerialized_Instrumentation.java | 4 + .../SinkManyBestEffort_Instrumentation.java | 4 + .../SinkManySerialized_Instrumentation.java | 4 + ...anySinkNoBackpressure_Instrumentation.java | 4 + .../UnicastProcessor_Instrumentation.java | 3 + .../publisher/FluxCreate_Instrumentation.java | 4 + .../publisher/MonoCreate_Instrumentation.java | 4 + .../ReplayProcessor_Instrumentation.java | 4 + .../SinkEmptyMulticast_Instrumentation.java | 4 + .../SinkEmptySerialized_Instrumentation.java | 4 + .../SinkManyBestEffort_Instrumentation.java | 4 + .../SinkManySerialized_Instrumentation.java | 4 + settings.gradle | 3 - 44 files changed, 113 insertions(+), 908 deletions(-) delete mode 100644 instrumentation/netty-reactor-0.8.0/NOTICE.txt delete mode 100644 instrumentation/netty-reactor-0.8.0/README.md delete mode 100644 instrumentation/netty-reactor-0.8.0/build.gradle delete mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.8.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java delete mode 100644 instrumentation/netty-reactor-0.9.0/NOTICE.txt delete mode 100644 instrumentation/netty-reactor-0.9.0/README.md delete mode 100644 instrumentation/netty-reactor-0.9.0/build.gradle delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java delete mode 100644 instrumentation/netty-reactor-0.9.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java diff --git a/instrumentation/netty-reactor-0.8.0/NOTICE.txt b/instrumentation/netty-reactor-0.8.0/NOTICE.txt deleted file mode 100644 index 2a0270a9c2..0000000000 --- a/instrumentation/netty-reactor-0.8.0/NOTICE.txt +++ /dev/null @@ -1,17 +0,0 @@ -This product contains a modified part of OpenTelemetry: - - * License: - -Copyright 2019 The OpenTelemetry 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. - - * Homepage: https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/LICENSE diff --git a/instrumentation/netty-reactor-0.8.0/README.md b/instrumentation/netty-reactor-0.8.0/README.md deleted file mode 100644 index 6e8c9267be..0000000000 --- a/instrumentation/netty-reactor-0.8.0/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# Reactor Netty Instrumentation - -Instrumentation for Reactor Netty server and also some widely used Reactor Core library code. - -This module is largely responsible for instrumenting the Reactor Core library to facilitate the passing, retrieval, -and linking of `Tokens` across contexts to tie asynchronous threads together for individual `Transactions`. - -This instrumentation is dependent on other instrumentation modules to start a `Transaction`. -Typically, the `netty-n.n` modules work with this instrumentation and will start a `Transaction` (see `NettyDispatcher#channelRead`). - -Most commonly this instrumentation comes into play with SpringBoot usage, in which case the `spring` and `spring-webflux` -instrumentation modules also apply and should result in `Transactions` being renamed after the Spring controller. - -## Key Components - -* `TokenLinkingSubscriber` - Implementation of a `reactor.core.CoreSubscriber` (a `Context` aware subscriber) that can be added as - a lifecycle hook on `Flux`/`Mono` operators to propagate, retrieve, and link `Tokens` across async contexts. This is done in various places as follows: - - ```java - if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { - Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); - } - ``` - -* `Schedulers_Instrumentation` and `HttpTrafficHandler_Instrumentation` - Both of these classes serve as entry points to add the `TokenLinkingSubscriber` sub-hook. - -* Scheduler `Task`s - Reactor Core Scheduler tasks that execute on asynchronous threads. These are instrumented as points to link `Tokens`. - -## Troubleshooting - -In cases where a `Transaction` gets named `/NettyDispatcher` (or named after a security `Filter`) it usually indicates that context was lost somewhere in -reactor code and that activity on threads where other instrumentation would typically apply could not be linked to the originating `Transaction` thread. -Figuring out how to propagate and link a `Token` across the threads should resolve the issue. diff --git a/instrumentation/netty-reactor-0.8.0/build.gradle b/instrumentation/netty-reactor-0.8.0/build.gradle deleted file mode 100644 index 713d5e8969..0000000000 --- a/instrumentation/netty-reactor-0.8.0/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -dependencies { - implementation(project(":agent-bridge")) - implementation("io.projectreactor.netty:reactor-netty:0.8.0.RELEASE") -} - - - -jar { - manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.netty-reactor-0.8.0' } -} - -verifyInstrumentation { - passesOnly 'io.projectreactor.netty:reactor-netty:[0.8.0.RELEASE,0.9.0.RELEASE)' -} - -site { - title 'Netty Reactor' - type 'Appserver' -} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java deleted file mode 100644 index 5eb65e3d1d..0000000000 --- a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * * Copyright 2022 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ -package reactor.netty.http.client; - -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import reactor.netty.Connection; -import reactor.util.context.Context; - -@Weave(originalName = "reactor.netty.http.client.HttpClientConnect") -final class HttpClientConnect_Instrumentation { - - @Weave(originalName = "reactor.netty.http.client.HttpClientConnect$HttpObserver") - static final class HttpObserver_Instrumentation { - - public Context currentContext() { - return Weaver.callOriginal(); - } - - @Trace(async = true, excludeFromTransactionTrace = true) - public void onUncaughtException(Connection connection, Throwable error) { - Context ctx = currentContext(); - Token token = ctx != null ? ctx.getOrDefault("newrelic-token", null) : null; - if (token != null && token.isActive()) { - token.link(); - } - Weaver.callOriginal(); - } - } -} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java deleted file mode 100644 index d998d6d2e4..0000000000 --- a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.netty.http.server; - -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber; -import io.netty.channel.ChannelHandlerContext; -import reactor.core.publisher.Hooks; -import reactor.core.publisher.Hooks_Instrumentation; - -import static com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber.tokenLift; - -@Weave(originalName = "reactor.netty.http.server.HttpTrafficHandler") -class HttpTrafficHandler_Instrumentation { - public void channelRead(ChannelHandlerContext ctx, Object msg) { - - /* - * Add tokenLift hook if it hasn't already been added. This allows for tokens to be retrieved from - * the current context and linked across threads at various points of the Flux/Mono lifecycle. - * - * This hook will only be added when using Netty Reactor with SpringBoot. When using other embedded web - * servers (e.g. Tomcat, Jetty, Undertow) the Schedulers_Instrumentation class will add the hook. - */ - if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { - Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); - } - Weaver.callOriginal(); - } -} diff --git a/instrumentation/netty-reactor-0.8.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java b/instrumentation/netty-reactor-0.8.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java deleted file mode 100644 index 14d498ebd2..0000000000 --- a/instrumentation/netty-reactor-0.8.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java +++ /dev/null @@ -1,246 +0,0 @@ -package com.nr.agent.instrumentation; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.agent.bridge.Token; -import com.newrelic.agent.introspec.InstrumentationTestConfig; -import com.newrelic.agent.introspec.InstrumentationTestRunner; -import com.newrelic.agent.introspec.Introspector; -import com.newrelic.api.agent.Trace; -import com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Hooks; -import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; -import reactor.util.context.Context; - -import java.time.Duration; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; - -import static com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber.tokenLift; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertTrue; - -@SuppressWarnings("deprecation") -@RunWith(InstrumentationTestRunner.class) -@InstrumentationTestConfig(includePrefixes = {"reactor"}) -public class TransactionPropagationTest { - - public static final String A_VALUE = ""; - - @BeforeClass - public static void init() { - Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); - } - - @Test - public void syncPropagationSanityCheck() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> - checkTransaction(hadTransaction)); - assertCapturedData(hadTransaction); - } - - @Test - public void asyncPropagationSanityCheck() throws InterruptedException { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - inAnotherThread(() -> - inAnnotatedWithTraceAsync(() -> { - token.linkAndExpire(); - checkTransaction(hadTransaction); - done.countDown(); - }) - ); - }); - done.await(); - assertCapturedData(hadTransaction); - } - - @Test - public void testReactorSchedulersInstrumentation() throws InterruptedException { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - Schedulers.elastic().schedule(() -> { -// trace_async(() -> { it is not need as Tasks are instrumented and annotated with @Trace(async = ture) - token.linkAndExpire(); - checkTransaction(hadTransaction); - done.countDown(); -// }); - }); - }); - done.await(); - assertCapturedData(hadTransaction); - } - - @Test - public void testEmptyMonoOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - Mono.empty() - .subscribeOn(Schedulers.elastic()) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testEmptyFluxOnComplete() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - Flux.empty() - .subscribeOn(Schedulers.elastic()) - .doOnComplete(() -> - checkTransaction(hadTransaction)) - .subscriberContext(with(token)) - .blockFirst(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testMonoOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - Mono.just(A_VALUE) - .subscribeOn(Schedulers.elastic()) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testMonoRetryOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - AtomicBoolean firstCall = new AtomicBoolean(true); - Mono - .create(monoSink -> - inAnotherThread(() -> { - if (firstCall.getAndSet(false)) - monoSink.error(new RuntimeException("failing the first call")); - else - monoSink.success(A_VALUE); - }) - ) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - .retry(2) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testMonoRetryBackoffOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - AtomicBoolean firstCall = new AtomicBoolean(true); - Mono - .create(monoSink -> - inAnotherThread(() -> { - if (firstCall.getAndSet(false)) - monoSink.error(new RuntimeException("failing the first call")); - else - monoSink.success(A_VALUE); - }) - ) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - .retryBackoff(2, Duration.ZERO) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testMonoNestedInFlatMap() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - Mono.just(A_VALUE) - .subscribeOn(Schedulers.elastic()) - .flatMap(v -> - Mono.just(A_VALUE) - .subscribeOn(Schedulers.elastic()) - .doOnSuccess(v2 -> - checkTransaction(hadTransaction))) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Trace(dispatcher = true) - public void inTransaction(Runnable actions) { - actions.run(); - } - - public void inAnotherThread(Runnable runnable) { - new Thread(runnable).start(); - } - - @Trace(async = true) - public void inAnnotatedWithTraceAsync(Runnable runnable) { - runnable.run(); - } - - public Token createToken() { - return AgentBridge.getAgent().getTransaction(false).getToken(); - } - - public Context with(Token token) { - return Context.empty().put("newrelic-token", token); - } - - @Trace - public void checkTransaction(AtomicBoolean hadTransaction) { - hadTransaction.set(AgentBridge.getAgent().getTransaction(false) != null); - } - - private void assertCapturedData(AtomicBoolean hadTransaction) { - assertTrue("Did not have transaction", hadTransaction.get()); - - Introspector introspector = InstrumentationTestRunner.getIntrospector(); - - assertThat("No finished transactions", introspector.getFinishedTransactionCount(), - is(greaterThan(0))); - - assertThat("Transaction names", introspector.getTransactionNames(), contains( - "OtherTransaction/Custom/" + getClass().getName() + "/inTransaction" - )); - - assertThat("Unscoped metrics", introspector.getUnscopedMetrics().keySet(), hasItems( - "Java/" + getClass().getName() + "/inTransaction", - "Custom/" + getClass().getName() + "/checkTransaction" - )); - } -} diff --git a/instrumentation/netty-reactor-0.9.0/NOTICE.txt b/instrumentation/netty-reactor-0.9.0/NOTICE.txt deleted file mode 100644 index 2a0270a9c2..0000000000 --- a/instrumentation/netty-reactor-0.9.0/NOTICE.txt +++ /dev/null @@ -1,17 +0,0 @@ -This product contains a modified part of OpenTelemetry: - - * License: - -Copyright 2019 The OpenTelemetry 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. - - * Homepage: https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/LICENSE diff --git a/instrumentation/netty-reactor-0.9.0/README.md b/instrumentation/netty-reactor-0.9.0/README.md deleted file mode 100644 index 6e8c9267be..0000000000 --- a/instrumentation/netty-reactor-0.9.0/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# Reactor Netty Instrumentation - -Instrumentation for Reactor Netty server and also some widely used Reactor Core library code. - -This module is largely responsible for instrumenting the Reactor Core library to facilitate the passing, retrieval, -and linking of `Tokens` across contexts to tie asynchronous threads together for individual `Transactions`. - -This instrumentation is dependent on other instrumentation modules to start a `Transaction`. -Typically, the `netty-n.n` modules work with this instrumentation and will start a `Transaction` (see `NettyDispatcher#channelRead`). - -Most commonly this instrumentation comes into play with SpringBoot usage, in which case the `spring` and `spring-webflux` -instrumentation modules also apply and should result in `Transactions` being renamed after the Spring controller. - -## Key Components - -* `TokenLinkingSubscriber` - Implementation of a `reactor.core.CoreSubscriber` (a `Context` aware subscriber) that can be added as - a lifecycle hook on `Flux`/`Mono` operators to propagate, retrieve, and link `Tokens` across async contexts. This is done in various places as follows: - - ```java - if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { - Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); - } - ``` - -* `Schedulers_Instrumentation` and `HttpTrafficHandler_Instrumentation` - Both of these classes serve as entry points to add the `TokenLinkingSubscriber` sub-hook. - -* Scheduler `Task`s - Reactor Core Scheduler tasks that execute on asynchronous threads. These are instrumented as points to link `Tokens`. - -## Troubleshooting - -In cases where a `Transaction` gets named `/NettyDispatcher` (or named after a security `Filter`) it usually indicates that context was lost somewhere in -reactor code and that activity on threads where other instrumentation would typically apply could not be linked to the originating `Transaction` thread. -Figuring out how to propagate and link a `Token` across the threads should resolve the issue. diff --git a/instrumentation/netty-reactor-0.9.0/build.gradle b/instrumentation/netty-reactor-0.9.0/build.gradle deleted file mode 100644 index 2189ccc502..0000000000 --- a/instrumentation/netty-reactor-0.9.0/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -dependencies { - implementation(project(":agent-bridge")) - implementation("io.projectreactor.netty:reactor-netty:0.9.0.RELEASE") -} - - - -jar { - manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.netty-reactor-0.9.0' } -} - -verifyInstrumentation { - passesOnly 'io.projectreactor.netty:reactor-netty:[0.9.0.RELEASE,)' -} - -site { - title 'Netty Reactor' - type 'Appserver' -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java b/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java deleted file mode 100644 index 07d671a6b2..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package com.nr.instrumentation; - -import com.newrelic.api.agent.NewRelic; - -public class NettyReactorConfig { - public static final boolean errorsEnabled = NewRelic.getAgent().getConfig() - .getValue("reactor-netty.errors.enabled", false); - - private NettyReactorConfig() { - } -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java deleted file mode 100644 index 5eb65e3d1d..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * * Copyright 2022 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ -package reactor.netty.http.client; - -import com.newrelic.api.agent.Token; -import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import reactor.netty.Connection; -import reactor.util.context.Context; - -@Weave(originalName = "reactor.netty.http.client.HttpClientConnect") -final class HttpClientConnect_Instrumentation { - - @Weave(originalName = "reactor.netty.http.client.HttpClientConnect$HttpObserver") - static final class HttpObserver_Instrumentation { - - public Context currentContext() { - return Weaver.callOriginal(); - } - - @Trace(async = true, excludeFromTransactionTrace = true) - public void onUncaughtException(Connection connection, Throwable error) { - Context ctx = currentContext(); - Token token = ctx != null ? ctx.getOrDefault("newrelic-token", null) : null; - if (token != null && token.isActive()) { - token.link(); - } - Weaver.callOriginal(); - } - } -} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java deleted file mode 100644 index d998d6d2e4..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package reactor.netty.http.server; - -import com.newrelic.api.agent.weaver.Weave; -import com.newrelic.api.agent.weaver.Weaver; -import com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber; -import io.netty.channel.ChannelHandlerContext; -import reactor.core.publisher.Hooks; -import reactor.core.publisher.Hooks_Instrumentation; - -import static com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber.tokenLift; - -@Weave(originalName = "reactor.netty.http.server.HttpTrafficHandler") -class HttpTrafficHandler_Instrumentation { - public void channelRead(ChannelHandlerContext ctx, Object msg) { - - /* - * Add tokenLift hook if it hasn't already been added. This allows for tokens to be retrieved from - * the current context and linked across threads at various points of the Flux/Mono lifecycle. - * - * This hook will only be added when using Netty Reactor with SpringBoot. When using other embedded web - * servers (e.g. Tomcat, Jetty, Undertow) the Schedulers_Instrumentation class will add the hook. - */ - if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { - Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); - } - Weaver.callOriginal(); - } -} diff --git a/instrumentation/netty-reactor-0.9.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java b/instrumentation/netty-reactor-0.9.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java deleted file mode 100644 index 985994ae09..0000000000 --- a/instrumentation/netty-reactor-0.9.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java +++ /dev/null @@ -1,351 +0,0 @@ -package com.nr.agent.instrumentation; - -import com.newrelic.agent.bridge.AgentBridge; -import com.newrelic.agent.bridge.Token; -import com.newrelic.agent.introspec.InstrumentationTestConfig; -import com.newrelic.agent.introspec.InstrumentationTestRunner; -import com.newrelic.agent.introspec.Introspector; -import com.newrelic.api.agent.Trace; -import com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Hooks; -import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; -import reactor.util.context.Context; - -import java.time.Duration; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; - -import static com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber.tokenLift; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertTrue; - -@SuppressWarnings("deprecation") -@RunWith(InstrumentationTestRunner.class) -@InstrumentationTestConfig(includePrefixes = {"reactor"}) -public class TransactionPropagationTest { - - public static final String A_VALUE = ""; - - @BeforeClass - public static void init() { - Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); - } - - @Test - public void syncPropagationSanityCheck() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> - checkTransaction(hadTransaction)); - assertCapturedData(hadTransaction); - } - - @Test - public void asyncPropagationSanityCheck() throws InterruptedException { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - inAnotherThread(() -> - inAnnotatedWithTraceAsync(() -> { - token.linkAndExpire(); - checkTransaction(hadTransaction); - done.countDown(); - }) - ); - }); - done.await(); - assertCapturedData(hadTransaction); - } - - @Test - public void testReactorSchedulersInstrumentation() throws InterruptedException { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - Schedulers.elastic().schedule(() -> { -// trace_async(() -> { it is not need as Tasks are instrumented and annotated with @Trace(async = ture) - token.linkAndExpire(); - checkTransaction(hadTransaction); - done.countDown(); -// }); - }); - }); - done.await(); - assertCapturedData(hadTransaction); - } - - @Test - public void testEmptyMonoOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - Mono.empty() - .subscribeOn(Schedulers.elastic()) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testEmptyFluxOnComplete() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - Flux.empty() - .subscribeOn(Schedulers.elastic()) - .doOnComplete(() -> - checkTransaction(hadTransaction)) - .subscriberContext(with(token)) - .blockFirst(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testMonoOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - Mono.just(A_VALUE) - .subscribeOn(Schedulers.elastic()) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testMonoRetryOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - AtomicBoolean firstCall = new AtomicBoolean(true); - Mono - .create(monoSink -> - inAnotherThread(() -> { - if (firstCall.getAndSet(false)) - monoSink.error(new RuntimeException("failing the first call")); - else - monoSink.success(A_VALUE); - }) - ) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - .retry(2) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testMonoRetryBackoffOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - AtomicBoolean firstCall = new AtomicBoolean(true); - Mono - .create(monoSink -> - inAnotherThread(() -> { - if (firstCall.getAndSet(false)) - monoSink.error(new RuntimeException("failing the first call")); - else - monoSink.success(A_VALUE); - }) - ) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - .retryBackoff(2, Duration.ZERO) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test - public void testMonoNestedInFlatMap() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - inTransaction(() -> { - Token token = createToken(); - Mono.just(A_VALUE) - .subscribeOn(Schedulers.elastic()) - .flatMap(v -> - Mono.just(A_VALUE) - .subscribeOn(Schedulers.elastic()) - .doOnSuccess(v2 -> - checkTransaction(hadTransaction))) - .subscriberContext(with(token)) - .block(); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test(timeout = 10000L) - public void testLambdaMonoSubscriberOnSuccess() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - Mono.empty() - .subscribeOn(Schedulers.elastic()) - .doOnSuccess(v -> - checkTransaction(hadTransaction)) - - // it is not need as LambdaMonoSubscriber instrumentation creates token - // and puts it into the context - //.subscriberContext(with(token)) - - // Call countDown in onComplete to see that instrumentation code calls original method - .subscribe(nil(), nil(), done::countDown); - await(done); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test(timeout = 10000L) - public void testLambdaMonoSubscriberOnError() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - Mono.error(new RuntimeException()) - .subscribeOn(Schedulers.elastic()) - .doOnError(v -> - checkTransaction(hadTransaction)) - - // it is not need as LambdaMonoSubscriber instrumentation creates token - // and puts it into the context - //.subscriberContext(with(token)) - - // Call countDown in onError to see that instrumentation code calls original method - .subscribe(nil(), v -> done.countDown()); - await(done); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test(timeout = 10000L) - public void testLambdaSubscriberOnComplete() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - Flux.empty() - .subscribeOn(Schedulers.elastic()) - .doOnComplete(() -> - checkTransaction(hadTransaction)) - - // it is not need as LambdaSubscriber instrumentation creates token - // and puts it into the context - //.subscriberContext(with(token)) - - // Call countDown in onComplete to see that instrumentation code calls original method - .subscribe(nil(), nil(), done::countDown); - await(done); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Test(timeout = 10000L) - public void testLambdaSubscriberOnError() { - AtomicBoolean hadTransaction = new AtomicBoolean(); - CountDownLatch done = new CountDownLatch(1); - inTransaction(() -> { - Token token = createToken(); - Flux.error(new RuntimeException()) - .subscribeOn(Schedulers.elastic()) - .doOnError(v -> - checkTransaction(hadTransaction)) - - // it is not need as LambdaSubscriber instrumentation creates token - // and puts it into the context - //.subscriberContext(with(token)) - - // Call countDown in onError to see that instrumentation code calls original method - .subscribe(nil(), v -> done.countDown()); - await(done); - token.expire(); - }); - assertCapturedData(hadTransaction); - } - - @Trace(dispatcher = true) - public void inTransaction(Runnable actions) { - actions.run(); - } - - public void inAnotherThread(Runnable runnable) { - new Thread(runnable).start(); - } - - @Trace(async = true) - public void inAnnotatedWithTraceAsync(Runnable runnable) { - runnable.run(); - } - - public Token createToken() { - return AgentBridge.getAgent().getTransaction(false).getToken(); - } - - public Context with(Token token) { - return Context.empty().put("newrelic-token", token); - } - - @Trace - public void checkTransaction(AtomicBoolean hadTransaction) { - hadTransaction.set(AgentBridge.getAgent().getTransaction(false) != null); - } - - private Consumer nil() { - return v -> { - }; - } - - private void await(CountDownLatch done) { - try { - done.await(); - } catch (InterruptedException ignore) { - } - } - - private void assertCapturedData(AtomicBoolean hadTransaction) { - assertTrue("Did not have transaction", hadTransaction.get()); - - Introspector introspector = InstrumentationTestRunner.getIntrospector(); - - assertThat("No finished transactions", introspector.getFinishedTransactionCount(), - is(greaterThan(0))); - - assertThat("Transaction names", introspector.getTransactionNames(), contains( - "OtherTransaction/Custom/" + getClass().getName() + "/inTransaction" - )); - - assertThat("Unscoped metrics", introspector.getUnscopedMetrics().keySet(), hasItems( - "Java/" + getClass().getName() + "/inTransaction", - "Custom/" + getClass().getName() + "/checkTransaction" - )); - } -} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java index 325d953616..65c00b64b1 100644 --- a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -55,7 +55,6 @@ public void error(Throwable e) { token.linkAndExpire(); token = null; } - NewRelic.noticeError(e); Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java index 42c0db29c5..215fa23125 100644 --- a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -7,6 +7,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.CoreSubscriber; @Weave(originalName = "reactor.core.publisher.FluxCreate") @@ -44,7 +45,9 @@ public void complete() { @Trace(async=true) public void error(Throwable e) { - NewRelic.noticeError(e); + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(e); + } if (token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java index b9c91d1b81..5e36e8e739 100644 --- a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.CoreSubscriber; import reactor.util.annotation.Nullable; @@ -48,11 +49,13 @@ public void success(T value) { @Trace(async=true) public void error(Throwable e) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(e); + } if(token != null) { token.linkAndExpire(); token = null; } - NewRelic.noticeError(e); Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java index 687ed717ac..a3e3ebff32 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -7,6 +7,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.CoreSubscriber; @Weave(originalName = "reactor.core.publisher.FluxCreate") @@ -33,6 +34,9 @@ public void complete() { @Trace(async = true) public void error(Throwable e) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(e); + } if (token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java index 2bfddc1fc6..11df91ef56 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.CoreSubscriber; @Weave(originalName = "reactor.core.publisher.MonoCreate") @@ -32,6 +33,9 @@ public void cancel() { @Trace(async = true) public void error(Throwable e) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(e); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java index 28fc5dfbdf..8c1442962c 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.ReplayProcessor") public class ReplayProcessor_Instrumentation { @@ -28,6 +29,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java index f6fcf43025..1fde592f28 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.SinkEmptyMulticast") class SinkEmptyMulticast_Instrumentation { @@ -30,6 +31,9 @@ public Sinks.EmitResult tryEmitEmpty() { @Trace(async=true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java index b1f7464ae1..b8d40df41b 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java @@ -7,6 +7,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.SinkEmptySerialized", type = MatchType.BaseClass) class SinkEmptySerialized_Instrumentation { @@ -18,6 +19,9 @@ public Sinks.EmitResult tryEmitEmpty() { @Trace public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } return Weaver.callOriginal(); } } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java index c8ccb61da7..c8db0cefe9 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.SinkManyBestEffort") class SinkManyBestEffort_Instrumentation { @@ -28,6 +29,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java index dd90533e2c..bb50447e8e 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.SinkManySerialized") class SinkManySerialized_Instrumentation { @@ -28,6 +29,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java index 1454625c3b..d5d2a031dc 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.Disposable; import java.util.Queue; @@ -32,6 +33,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java index 31bfdea1a3..8b1f0ba8f9 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -47,6 +47,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java index 2d72a90ba3..6107f7297d 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -29,6 +29,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java index 687ed717ac..da12497ef9 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -7,6 +7,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.CoreSubscriber; @Weave(originalName = "reactor.core.publisher.FluxCreate") @@ -32,7 +33,10 @@ public void complete() { } @Trace(async = true) - public void error(Throwable e) { + public void error(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if (token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java index 2bfddc1fc6..11df91ef56 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.CoreSubscriber; @Weave(originalName = "reactor.core.publisher.MonoCreate") @@ -32,6 +33,9 @@ public void cancel() { @Trace(async = true) public void error(Throwable e) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(e); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java index 62b35e2f4d..c13c88e4a6 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java @@ -36,6 +36,9 @@ Sinks.EmitResult tryEmitValue(O value) { } public void onError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java index 50e1e42e6f..f4e9d6fd9c 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.ReplayProcessor") public class ReplayProcessor_Instrumentation { @@ -28,6 +29,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java index f6fcf43025..1fde592f28 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.SinkEmptyMulticast") class SinkEmptyMulticast_Instrumentation { @@ -30,6 +31,9 @@ public Sinks.EmitResult tryEmitEmpty() { @Trace(async=true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java index b1f7464ae1..b8d40df41b 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java @@ -7,6 +7,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.SinkEmptySerialized", type = MatchType.BaseClass) class SinkEmptySerialized_Instrumentation { @@ -18,6 +19,9 @@ public Sinks.EmitResult tryEmitEmpty() { @Trace public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } return Weaver.callOriginal(); } } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java index c8ccb61da7..c8db0cefe9 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.SinkManyBestEffort") class SinkManyBestEffort_Instrumentation { @@ -28,6 +29,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java index da6bb89e12..e20d9adc75 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.SinkManySerialized") class SinkManySerialized_Instrumentation { @@ -28,6 +29,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java index 1454625c3b..d5d2a031dc 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.Disposable; import java.util.Queue; @@ -32,6 +33,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java index 31bfdea1a3..8b1f0ba8f9 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -47,6 +47,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java index 687ed717ac..a3e3ebff32 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -7,6 +7,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.CoreSubscriber; @Weave(originalName = "reactor.core.publisher.FluxCreate") @@ -33,6 +34,9 @@ public void complete() { @Trace(async = true) public void error(Throwable e) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(e); + } if (token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java index 13982e5c01..954dfa3386 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.CoreSubscriber; import reactor.util.annotation.Nullable; @@ -33,6 +34,9 @@ public void cancel() { @Trace(async = true) public void error(Throwable e) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(e); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java index 50e1e42e6f..f4e9d6fd9c 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.ReplayProcessor") public class ReplayProcessor_Instrumentation { @@ -28,6 +29,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java index f6fcf43025..1fde592f28 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.SinkEmptyMulticast") class SinkEmptyMulticast_Instrumentation { @@ -30,6 +31,9 @@ public Sinks.EmitResult tryEmitEmpty() { @Trace(async=true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java index b1f7464ae1..b8d40df41b 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java @@ -7,6 +7,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.SinkEmptySerialized", type = MatchType.BaseClass) class SinkEmptySerialized_Instrumentation { @@ -18,6 +19,9 @@ public Sinks.EmitResult tryEmitEmpty() { @Trace public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } return Weaver.callOriginal(); } } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java index c8ccb61da7..c8db0cefe9 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.SinkManyBestEffort") class SinkManyBestEffort_Instrumentation { @@ -28,6 +29,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java index dd90533e2c..bb50447e8e 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java @@ -6,6 +6,7 @@ import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.ReactorConfig; @Weave(originalName = "reactor.core.publisher.SinkManySerialized") class SinkManySerialized_Instrumentation { @@ -28,6 +29,9 @@ public Sinks.EmitResult tryEmitComplete() { @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { + if(ReactorConfig.errorsEnabled) { + NewRelic.noticeError(t); + } if(token != null) { token.linkAndExpire(); token = null; diff --git a/settings.gradle b/settings.gradle index c87b244e48..a9476a5c76 100644 --- a/settings.gradle +++ b/settings.gradle @@ -315,8 +315,6 @@ include 'instrumentation:netty-4.0.0' include 'instrumentation:netty-4.0.8' include 'instrumentation:netty-4.1.16' include 'instrumentation:netty-reactor-0.7.0' -include 'instrumentation:netty-reactor-0.8.0' -include 'instrumentation:netty-reactor-0.9.0' include 'instrumentation:okhttp-3.6.0' include 'instrumentation:okhttp-3.14.0' include 'instrumentation:okhttp-4.0.0' @@ -352,7 +350,6 @@ include 'instrumentation:r2dbc-postgresql-0.9.2' include 'instrumentation:r2dbc-mssql' include 'instrumentation:rabbit-amqp-2.7.0' include 'instrumentation:rabbit-amqp-5.0.0' -//include 'instrumentation:reactor-3.3.0' include 'instrumentation:reactor-core-3.1.0' include 'instrumentation:reactor-core-3.3.0' include 'instrumentation:reactor-core-3.4.0' From d780bb1be0753425ac94a6c12dfb5c8e2d2e408e Mon Sep 17 00:00:00 2001 From: Doug Hilpipre Date: Fri, 3 Apr 2026 15:22:24 -0500 Subject: [PATCH 4/7] fixed problems with unexpiring tokens and some cleanup --- .../EmitterProcessor_Instrumentation.java | 26 ++--------- .../NextProcessor_Instrumentation.java | 24 ++-------- .../ReplayProcessor_Instrumentation.java | 26 ++--------- .../SinkEmptyMulticast_Instrumentation.java | 23 +--------- .../SinkEmptySerialized_Instrumentation.java | 2 - .../SinkManyBestEffort_Instrumentation.java | 26 ++--------- .../SinkManySerialized_Instrumentation.java | 20 --------- ...anySinkNoBackpressure_Instrumentation.java | 30 ++----------- .../UnicastProcessor_Instrumentation.java | 44 ++----------------- .../EmitterProcessor_Instrumentation.java | 26 ++--------- .../NextProcessor_Instrumentation.java | 20 +-------- .../ReplayProcessor_Instrumentation.java | 26 ++--------- .../SinkEmptyMulticast_Instrumentation.java | 23 +--------- .../SinkEmptySerialized_Instrumentation.java | 2 - .../SinkManyBestEffort_Instrumentation.java | 26 ++--------- .../SinkManySerialized_Instrumentation.java | 26 ++--------- .../SinkOneMulticast_Instrumentation.java | 4 -- ...anySinkNoBackpressure_Instrumentation.java | 30 ++----------- .../UnicastProcessor_Instrumentation.java | 44 ++----------------- 19 files changed, 42 insertions(+), 406 deletions(-) diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java index 2d72a90ba3..e986ec6194 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,36 +9,18 @@ @Weave(originalName = "reactor.core.publisher.EmitterProcessor") public class EmitterProcessor_Instrumentation { - @NewField - private Token token; - - EmitterProcessor_Instrumentation(boolean autoCancel, int prefetch) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java index 2f04090f0b..56a0cfc855 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java @@ -1,44 +1,26 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; -import reactor.core.CorePublisher; @Weave(originalName = "reactor.core.publisher.NextProcessor") class NextProcessor_Instrumentation { - @NewField - private Token token; - - NextProcessor_Instrumentation(CorePublisher source) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable cause) { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitValue(O value) { - token.linkAndExpire(); - token = null; return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitEmpty() { - token.linkAndExpire(); - token = null; return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java index 8c1442962c..2191f9da4b 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,39 +9,21 @@ @Weave(originalName = "reactor.core.publisher.ReplayProcessor") public class ReplayProcessor_Instrumentation { - @NewField - private Token token; - - ReplayProcessor_Instrumentation(FluxReplay.ReplayBuffer buffer) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java index 1fde592f28..ac01283c87 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,33 +9,16 @@ @Weave(originalName = "reactor.core.publisher.SinkEmptyMulticast") class SinkEmptyMulticast_Instrumentation { - @NewField - protected Token token = null; - - SinkEmptyMulticast_Instrumentation() { - if(token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - @Trace(async=true) + @Trace public Sinks.EmitResult tryEmitEmpty() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async=true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java index b8d40df41b..2606e489a0 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java @@ -1,10 +1,8 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java index c8db0cefe9..6511007406 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,39 +9,21 @@ @Weave(originalName = "reactor.core.publisher.SinkManyBestEffort") class SinkManyBestEffort_Instrumentation { - @NewField - private Token token; - - SinkManyBestEffort_Instrumentation(boolean allOrNothing) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java index bb50447e8e..5ebe66f0de 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,19 +9,8 @@ @Weave(originalName = "reactor.core.publisher.SinkManySerialized") class SinkManySerialized_Instrumentation { - @NewField - private Token token; - - SinkManySerialized_Instrumentation(Sinks.Many sink, ContextHolder contextHolder) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - @Trace(async = true) public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } @@ -32,18 +19,11 @@ public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } @Trace(async = true) public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java index d5d2a031dc..4c46de1eae 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java @@ -1,53 +1,29 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; -import reactor.core.Disposable; - -import java.util.Queue; -import java.util.function.Consumer; @Weave(originalName = "reactor.core.publisher.UnicastManySinkNoBackpressure") class UnicastManySinkNoBackpressure_Instrumentation { - @NewField - private Token token; - - UnicastManySinkNoBackpressure_Instrumentation() { - token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java index 8b1f0ba8f9..780823a364 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -1,67 +1,29 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; -import reactor.core.Disposable; - -import java.util.Queue; -import java.util.function.Consumer; @Weave(originalName = "reactor.core.publisher.UnicastProcessor") public class UnicastProcessor_Instrumentation { - @NewField - private Token token; - - public UnicastProcessor_Instrumentation(Queue queue) { - if(token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - public UnicastProcessor_Instrumentation(Queue queue, Disposable onTerminate) { - if(token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - public UnicastProcessor_Instrumentation(Queue queue, Consumer onOverflow, Disposable onTerminate) { - if(token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java index 6107f7297d..bfaf66d3bf 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,39 +9,21 @@ @Weave(originalName = "reactor.core.publisher.EmitterProcessor") public class EmitterProcessor_Instrumentation { - @NewField - private Token token; - - EmitterProcessor_Instrumentation(boolean autoCancel, int prefetch) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java index c13c88e4a6..703b9c0e10 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java @@ -1,37 +1,21 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; -import reactor.core.CorePublisher; @Weave(originalName = "reactor.core.publisher.NextProcessor") class NextProcessor_Instrumentation { - @NewField - private Token token; - - NextProcessor_Instrumentation(CorePublisher source) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace Sinks.EmitResult tryEmitError(Throwable cause) { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace Sinks.EmitResult tryEmitValue(O value) { - token.linkAndExpire(); - token = null; return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java index f4e9d6fd9c..2191f9da4b 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,39 +9,21 @@ @Weave(originalName = "reactor.core.publisher.ReplayProcessor") public class ReplayProcessor_Instrumentation { - @NewField - private Token token; - - ReplayProcessor_Instrumentation(FluxReplay.ReplayBuffer buffer) { - this.token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java index 1fde592f28..ac01283c87 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,33 +9,16 @@ @Weave(originalName = "reactor.core.publisher.SinkEmptyMulticast") class SinkEmptyMulticast_Instrumentation { - @NewField - protected Token token = null; - - SinkEmptyMulticast_Instrumentation() { - if(token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - @Trace(async=true) + @Trace public Sinks.EmitResult tryEmitEmpty() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async=true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java index b8d40df41b..2606e489a0 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java @@ -1,10 +1,8 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java index c8db0cefe9..6511007406 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,39 +9,21 @@ @Weave(originalName = "reactor.core.publisher.SinkManyBestEffort") class SinkManyBestEffort_Instrumentation { - @NewField - private Token token; - - SinkManyBestEffort_Instrumentation(boolean allOrNothing) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java index e20d9adc75..cdde2f5e33 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,39 +9,21 @@ @Weave(originalName = "reactor.core.publisher.SinkManySerialized") class SinkManySerialized_Instrumentation { - @NewField - private Token token; - - SinkManySerialized_Instrumentation(Sinks.Many sink, ContextHolder contextHolder) { - this.token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java index f58b581bb8..3e4fb3e281 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java @@ -9,10 +9,6 @@ class SinkOneMulticast_Instrumentation extends SinkEmptyMulticast_Instrumenta @Trace(async=true) public Sinks.EmitResult tryEmitValue(O value) { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java index d5d2a031dc..4c46de1eae 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java @@ -1,53 +1,29 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; -import reactor.core.Disposable; - -import java.util.Queue; -import java.util.function.Consumer; @Weave(originalName = "reactor.core.publisher.UnicastManySinkNoBackpressure") class UnicastManySinkNoBackpressure_Instrumentation { - @NewField - private Token token; - - UnicastManySinkNoBackpressure_Instrumentation() { - token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java index 8b1f0ba8f9..780823a364 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -1,67 +1,29 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; -import reactor.core.Disposable; - -import java.util.Queue; -import java.util.function.Consumer; @Weave(originalName = "reactor.core.publisher.UnicastProcessor") public class UnicastProcessor_Instrumentation { - @NewField - private Token token; - - public UnicastProcessor_Instrumentation(Queue queue) { - if(token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - public UnicastProcessor_Instrumentation(Queue queue, Disposable onTerminate) { - if(token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - public UnicastProcessor_Instrumentation(Queue queue, Consumer onOverflow, Disposable onTerminate) { - if(token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } From 69f7a8ffd3b049453efc64b3c0f7e6c8252e0090 Mon Sep 17 00:00:00 2001 From: Doug Hilpipre Date: Tue, 7 Apr 2026 07:14:08 -0500 Subject: [PATCH 5/7] added skip for netty reactor --- .../instrumentation/NettyReactorConfig.java | 17 + .../TokenLinkingSubscriber.java | 100 +++++ .../core/publisher/Hooks_Instrumentation.java | 19 + .../HttpServerHandler_Instrumentation.java | 8 + .../netty-reactor-0.8.0/build.gradle | 19 + .../instrumentation/NettyReactorConfig.java | 18 + .../reactor/netty/TokenLinkingSubscriber.java | 107 ++++++ .../core/publisher/Hooks_Instrumentation.java | 25 ++ ...antPeriodicWorkerTask_Instrumentation.java | 23 ++ .../PeriodicWorkerTask_Instrumentation.java | 23 ++ .../SchedulerTask_Instrumentation.java | 23 ++ .../scheduler/Schedulers_Instrumentation.java | 45 +++ .../scheduler/WorkerTask_Instrumentation.java | 23 ++ .../HttpClientConnect_Instrumentation.java | 36 ++ .../http/server/HttpServerMetricsHandler.java | 7 + .../HttpTrafficHandler_Instrumentation.java | 35 ++ .../TransactionPropagationTest.java | 246 ++++++++++++ .../netty-reactor-0.9.0/build.gradle | 19 + .../instrumentation/NettyReactorConfig.java | 18 + .../instrumentation/SubscriptionWrapper.java | 37 ++ .../reactor/netty/TokenLinkingSubscriber.java | 107 ++++++ .../FluxMapFuseable_Instrumentation.java | 68 ++++ .../core/publisher/Hooks_Instrumentation.java | 25 ++ .../LambdaMonoSubscriber_Instrumentation.java | 78 ++++ .../LambdaSubscriber_Instrumentation.java | 77 ++++ .../MonoSubscribeOn_Instrumentation.java | 73 ++++ ...antPeriodicWorkerTask_Instrumentation.java | 23 ++ .../PeriodicWorkerTask_Instrumentation.java | 23 ++ .../SchedulerTask_Instrumentation.java | 23 ++ .../scheduler/Schedulers_Instrumentation.java | 44 +++ .../scheduler/WorkerTask_Instrumentation.java | 23 ++ .../HttpClientConnect_Instrumentation.java | 36 ++ .../HttpTrafficHandler_Instrumentation.java | 35 ++ .../TransactionPropagationTest.java | 351 ++++++++++++++++++ .../http/server/HttpTrafficHandler_Skip.java | 15 + .../http/server/HttpTrafficHandler_Skip.java | 15 + .../http/server/HttpTrafficHandler_Skip.java | 15 + .../http/server/HttpTrafficHandler_Skip.java | 15 + .../http/server/HttpTrafficHandler_Skip.java | 15 + settings.gradle | 2 + 40 files changed, 1911 insertions(+) create mode 100644 instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java create mode 100644 instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/TokenLinkingSubscriber.java create mode 100644 instrumentation/netty-reactor-0.7.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.8.0/build.gradle create mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java create mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java create mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpServerMetricsHandler.java create mode 100644 instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.8.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java create mode 100644 instrumentation/netty-reactor-0.9.0/build.gradle create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/SubscriptionWrapper.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/FluxMapFuseable_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaMonoSubscriber_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaSubscriber_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/MonoSubscribeOn_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java create mode 100644 instrumentation/netty-reactor-0.9.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java create mode 100644 instrumentation/reactor-core-3.1.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java create mode 100644 instrumentation/reactor-core-3.3.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java create mode 100644 instrumentation/reactor-core-3.4.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java create mode 100644 instrumentation/reactor-core-3.4.10/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java create mode 100644 instrumentation/reactor-core-3.5.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java diff --git a/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java b/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java new file mode 100644 index 0000000000..6a0a3090ee --- /dev/null +++ b/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java @@ -0,0 +1,17 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation; + +import com.newrelic.api.agent.NewRelic; + +public class NettyReactorConfig { + public static final boolean errorsEnabled = NewRelic.getAgent().getConfig().getValue("reactor-netty.errors.enabled", false); + + private NettyReactorConfig() { + } +} diff --git a/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/TokenLinkingSubscriber.java b/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/TokenLinkingSubscriber.java new file mode 100644 index 0000000000..186e520b35 --- /dev/null +++ b/instrumentation/netty-reactor-0.7.0/src/main/java/com/nr/instrumentation/TokenLinkingSubscriber.java @@ -0,0 +1,100 @@ +package com.nr.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; +import reactor.core.Fuseable; +import reactor.core.Scannable; +import reactor.core.publisher.Operators; +import reactor.util.context.Context; + +import java.util.function.BiFunction; +import java.util.function.Function; + +// Based on OpenTelemetry code +// https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/TracingSubscriber.java +public class TokenLinkingSubscriber implements CoreSubscriber { + private final Token token; + private final Subscriber subscriber; + private Context context; + + public TokenLinkingSubscriber(Subscriber subscriber, Context ctx) { + this.subscriber = subscriber; + this.context = ctx; + // newrelic-token is added by spring-webflux-5.1 instrumentation of ServerWebExchange + this.token = ctx.getOrDefault("newrelic-token", null); + } + + @Override + public void onSubscribe(Subscription subscription) { + subscriber.onSubscribe(subscription); + } + + @Override + public void onNext(T o) { + withNRToken(() -> subscriber.onNext(o)); + } + + @Override + public void onError(Throwable throwable) { + withNRError(() -> subscriber.onError(throwable), throwable); + } + + @Override + public void onComplete() { + subscriber.onComplete(); + } + + @Override + public Context currentContext() { + return context; + } + + @Trace(async = true, excludeFromTransactionTrace = true) + private void withNRToken(Runnable runnable) { + if (token != null && AgentBridge.getAgent().getTransaction(false) == null) { + token.link(); + } + runnable.run(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + private void withNRError(Runnable runnable, Throwable throwable) { + if (token != null && token.isActive()) { + token.linkAndExpire(); + if (NettyReactorConfig.errorsEnabled) { + NewRelic.noticeError(throwable); + } + } + runnable.run(); + } + + public static Function, ? extends Publisher> tokenLift() { + return Operators.lift(new TokenLifter<>()); + } + + private static class TokenLifter + implements BiFunction, CoreSubscriber> { + + public TokenLifter() { + } + + @Override + public CoreSubscriber apply(Scannable publisher, CoreSubscriber sub) { + // if Flux/Mono #just, #empty, #error + if (publisher instanceof Fuseable.ScalarCallable) { + return sub; + } + Token token = sub.currentContext().getOrDefault("newrelic-token", null); + if (token != null ) { + return new TokenLinkingSubscriber<>(sub, sub.currentContext()); + } + return sub; + } + } +} \ No newline at end of file diff --git a/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java b/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java new file mode 100644 index 0000000000..abe1cd448c --- /dev/null +++ b/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java @@ -0,0 +1,19 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.publisher; + +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; + +import java.util.concurrent.atomic.AtomicBoolean; + +@Weave(originalName = "reactor.core.publisher.Hooks") +public abstract class Hooks_Instrumentation { + @NewField + public static AtomicBoolean instrumented = new AtomicBoolean(false); +} diff --git a/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/ipc/netty/http/server/HttpServerHandler_Instrumentation.java b/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/ipc/netty/http/server/HttpServerHandler_Instrumentation.java index 380e77977d..08d9f56289 100644 --- a/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/ipc/netty/http/server/HttpServerHandler_Instrumentation.java +++ b/instrumentation/netty-reactor-0.7.0/src/main/java/reactor/ipc/netty/http/server/HttpServerHandler_Instrumentation.java @@ -11,13 +11,21 @@ import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.TokenLinkingSubscriber; import io.netty.channel.ChannelHandlerContext_Instrumentation; import io.netty.handler.codec.http.HttpRequest; +import reactor.core.publisher.Hooks; +import reactor.core.publisher.Hooks_Instrumentation; + +import static com.nr.instrumentation.TokenLinkingSubscriber.tokenLift; @Weave(type = MatchType.BaseClass, originalName = "reactor.ipc.netty.http.server.HttpServerHandler") class HttpServerHandler_Instrumentation { public void channelRead(ChannelHandlerContext_Instrumentation ctx, Object msg) { + if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { + Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); + } Weaver.callOriginal(); if (msg instanceof HttpRequest) { if (ctx.pipeline().reactiveLayerToken == null) { diff --git a/instrumentation/netty-reactor-0.8.0/build.gradle b/instrumentation/netty-reactor-0.8.0/build.gradle new file mode 100644 index 0000000000..713d5e8969 --- /dev/null +++ b/instrumentation/netty-reactor-0.8.0/build.gradle @@ -0,0 +1,19 @@ +dependencies { + implementation(project(":agent-bridge")) + implementation("io.projectreactor.netty:reactor-netty:0.8.0.RELEASE") +} + + + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.netty-reactor-0.8.0' } +} + +verifyInstrumentation { + passesOnly 'io.projectreactor.netty:reactor-netty:[0.8.0.RELEASE,0.9.0.RELEASE)' +} + +site { + title 'Netty Reactor' + type 'Appserver' +} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java b/instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java new file mode 100644 index 0000000000..07d671a6b2 --- /dev/null +++ b/instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java @@ -0,0 +1,18 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation; + +import com.newrelic.api.agent.NewRelic; + +public class NettyReactorConfig { + public static final boolean errorsEnabled = NewRelic.getAgent().getConfig() + .getValue("reactor-netty.errors.enabled", false); + + private NettyReactorConfig() { + } +} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java b/instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java new file mode 100644 index 0000000000..541977f380 --- /dev/null +++ b/instrumentation/netty-reactor-0.8.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java @@ -0,0 +1,107 @@ +package com.nr.instrumentation.reactor.netty; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.nr.instrumentation.NettyReactorConfig; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; +import reactor.core.Fuseable; +import reactor.core.Scannable; +import reactor.core.publisher.Operators; +import reactor.util.context.Context; + +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * Implementation of a reactor.core.CoreSubscriber (a Context aware subscriber) that can be added as + * a lifecycle hook on Flux/Mono operators to propagate, retrieve, and link Tokens across async contexts. + * + * Based on OpenTelemetry code: + * https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/TracingSubscriber.java + * @param + */ +public class TokenLinkingSubscriber implements CoreSubscriber { + private final Token token; + private final Subscriber subscriber; + private Context context; + + public TokenLinkingSubscriber(Subscriber subscriber, Context ctx) { + this.subscriber = subscriber; + this.context = ctx; + // newrelic-token is added by spring-webflux instrumentation of ServerWebExchange + this.token = ctx.getOrDefault("newrelic-token", null); + } + + @Override + public void onSubscribe(Subscription subscription) { + withNRToken(() -> subscriber.onSubscribe(subscription)); + } + + @Override + public void onNext(T o) { + withNRToken(() -> subscriber.onNext(o)); + } + + @Override + public void onError(Throwable throwable) { + withNRError(() -> subscriber.onError(throwable), throwable); + } + + @Override + public void onComplete() { + withNRToken(subscriber::onComplete); + } + + @Override + public Context currentContext() { + return context; + } + + @Trace(async = true, excludeFromTransactionTrace = true) + private void withNRToken(Runnable runnable) { + if (token != null && AgentBridge.getAgent().getTransaction(false) == null) { + token.link(); + } + runnable.run(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + private void withNRError(Runnable runnable, Throwable throwable) { + if (token != null && token.isActive()) { + token.link(); + if (NettyReactorConfig.errorsEnabled) { + NewRelic.noticeError(throwable); + } + } + runnable.run(); + } + + public static Function, ? extends Publisher> tokenLift() { + return Operators.lift(new TokenLifter<>()); + } + + private static class TokenLifter + implements BiFunction, CoreSubscriber> { + + public TokenLifter() { + } + + @Override + public CoreSubscriber apply(Scannable publisher, CoreSubscriber sub) { + // if Flux/Mono #just, #empty, #error + if (publisher instanceof Fuseable.ScalarCallable) { + return sub; + } + Token token = sub.currentContext().getOrDefault("newrelic-token", null); + if (token != null ) { + return new TokenLinkingSubscriber<>(sub, sub.currentContext()); + } + return sub; + } + } +} \ No newline at end of file diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java new file mode 100644 index 0000000000..b573bfefe9 --- /dev/null +++ b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java @@ -0,0 +1,25 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.publisher; + +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; + +import java.util.concurrent.atomic.AtomicBoolean; + +@Weave(originalName = "reactor.core.publisher.Hooks") +public abstract class Hooks_Instrumentation { + + /* + * Note that sub-hooks are cumulative. We want to avoid setting the same sub-hooks + * more than once, so we set this boolean to true the first time we set a sub-hook. + * if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { Hooks.onEachOperator(...) } + */ + @NewField + public static AtomicBoolean instrumented = new AtomicBoolean(false); +} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java new file mode 100644 index 0000000000..4095149fa9 --- /dev/null +++ b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java @@ -0,0 +1,23 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.scheduler; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.scheduler.InstantPeriodicWorkerTask") +final class InstantPeriodicWorkerTask_Instrumentation { + + // We need to be able to link the Token here when executing on a supplied Scheduler + // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator + @Trace(async = true, excludeFromTransactionTrace = true) + public Void call() { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java new file mode 100644 index 0000000000..988d33ce26 --- /dev/null +++ b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java @@ -0,0 +1,23 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.scheduler; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.scheduler.PeriodicWorkerTask") +final class PeriodicWorkerTask_Instrumentation { + + // We need to be able to link the Token here when executing on a supplied Scheduler + // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator + @Trace(async = true, excludeFromTransactionTrace = true) + public Void call() { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java new file mode 100644 index 0000000000..fda06f032a --- /dev/null +++ b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java @@ -0,0 +1,23 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.scheduler; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.scheduler.SchedulerTask") +final class SchedulerTask_Instrumentation { + + // We need to be able to link the Token here when executing on a supplied Scheduler via Mono::publishOn + // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator + @Trace(async = true, excludeFromTransactionTrace = true) + public Void call() { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java new file mode 100644 index 0000000000..29ae1e901a --- /dev/null +++ b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java @@ -0,0 +1,45 @@ +/* + * Copyright 2021 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package reactor.core.scheduler; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber; +import reactor.core.publisher.Hooks; +import reactor.core.publisher.Hooks_Instrumentation; + +import static com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber.tokenLift; + +@Weave(type = MatchType.BaseClass, originalName = "reactor.core.scheduler.Schedulers") +public abstract class Schedulers_Instrumentation { + + @Weave(type = MatchType.ExactClass, originalName = "reactor.core.scheduler.Schedulers$CachedScheduler") + static class CachedScheduler { + final Scheduler cached; + final String key; + + CachedScheduler(String key, Scheduler cached) { + /* + * Add tokenLift hook if it hasn't already been added. This allows for tokens to be retrieved from + * the current context and linked across threads at various points of the Flux/Mono lifecycle. + * + * When using Netty Reactor with SpringBoot this hook will be added by the HttpTrafficHandler_Instrumentation + * but when using other embedded web servers (e.g. Tomcat, Jetty, Undertow) the HttpTrafficHandler class + * doesn't get loaded and thus the hook isn't added. This ensures that the hook is added in a common code + * path before any Scheduler Tasks are spun off on new threads. + */ + if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { + Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); + } + + this.cached = Weaver.callOriginal(); + this.key = Weaver.callOriginal(); + } + + } + +} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java new file mode 100644 index 0000000000..76867aca47 --- /dev/null +++ b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java @@ -0,0 +1,23 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.scheduler; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.scheduler.WorkerTask") +final class WorkerTask_Instrumentation { + + // We need to be able to link the Token here when executing on a supplied Scheduler + // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator + @Trace(async = true, excludeFromTransactionTrace = true) + public Void call() { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java new file mode 100644 index 0000000000..5eb65e3d1d --- /dev/null +++ b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java @@ -0,0 +1,36 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package reactor.netty.http.client; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.netty.Connection; +import reactor.util.context.Context; + +@Weave(originalName = "reactor.netty.http.client.HttpClientConnect") +final class HttpClientConnect_Instrumentation { + + @Weave(originalName = "reactor.netty.http.client.HttpClientConnect$HttpObserver") + static final class HttpObserver_Instrumentation { + + public Context currentContext() { + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public void onUncaughtException(Connection connection, Throwable error) { + Context ctx = currentContext(); + Token token = ctx != null ? ctx.getOrDefault("newrelic-token", null) : null; + if (token != null && token.isActive()) { + token.link(); + } + Weaver.callOriginal(); + } + } +} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpServerMetricsHandler.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpServerMetricsHandler.java new file mode 100644 index 0000000000..65a36cd64b --- /dev/null +++ b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpServerMetricsHandler.java @@ -0,0 +1,7 @@ +package reactor.netty.http.server; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +@SkipIfPresent +final class HttpServerMetricsHandler { +} diff --git a/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java new file mode 100644 index 0000000000..d998d6d2e4 --- /dev/null +++ b/instrumentation/netty-reactor-0.8.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java @@ -0,0 +1,35 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.netty.http.server; + +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber; +import io.netty.channel.ChannelHandlerContext; +import reactor.core.publisher.Hooks; +import reactor.core.publisher.Hooks_Instrumentation; + +import static com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber.tokenLift; + +@Weave(originalName = "reactor.netty.http.server.HttpTrafficHandler") +class HttpTrafficHandler_Instrumentation { + public void channelRead(ChannelHandlerContext ctx, Object msg) { + + /* + * Add tokenLift hook if it hasn't already been added. This allows for tokens to be retrieved from + * the current context and linked across threads at various points of the Flux/Mono lifecycle. + * + * This hook will only be added when using Netty Reactor with SpringBoot. When using other embedded web + * servers (e.g. Tomcat, Jetty, Undertow) the Schedulers_Instrumentation class will add the hook. + */ + if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { + Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-reactor-0.8.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java b/instrumentation/netty-reactor-0.8.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java new file mode 100644 index 0000000000..14d498ebd2 --- /dev/null +++ b/instrumentation/netty-reactor-0.8.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java @@ -0,0 +1,246 @@ +package com.nr.agent.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Token; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.api.agent.Trace; +import com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Hooks; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; +import reactor.util.context.Context; + +import java.time.Duration; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + +import static com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber.tokenLift; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertTrue; + +@SuppressWarnings("deprecation") +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = {"reactor"}) +public class TransactionPropagationTest { + + public static final String A_VALUE = ""; + + @BeforeClass + public static void init() { + Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); + } + + @Test + public void syncPropagationSanityCheck() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> + checkTransaction(hadTransaction)); + assertCapturedData(hadTransaction); + } + + @Test + public void asyncPropagationSanityCheck() throws InterruptedException { + AtomicBoolean hadTransaction = new AtomicBoolean(); + CountDownLatch done = new CountDownLatch(1); + inTransaction(() -> { + Token token = createToken(); + inAnotherThread(() -> + inAnnotatedWithTraceAsync(() -> { + token.linkAndExpire(); + checkTransaction(hadTransaction); + done.countDown(); + }) + ); + }); + done.await(); + assertCapturedData(hadTransaction); + } + + @Test + public void testReactorSchedulersInstrumentation() throws InterruptedException { + AtomicBoolean hadTransaction = new AtomicBoolean(); + CountDownLatch done = new CountDownLatch(1); + inTransaction(() -> { + Token token = createToken(); + Schedulers.elastic().schedule(() -> { +// trace_async(() -> { it is not need as Tasks are instrumented and annotated with @Trace(async = ture) + token.linkAndExpire(); + checkTransaction(hadTransaction); + done.countDown(); +// }); + }); + }); + done.await(); + assertCapturedData(hadTransaction); + } + + @Test + public void testEmptyMonoOnSuccess() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> { + Token token = createToken(); + Mono.empty() + .subscribeOn(Schedulers.elastic()) + .doOnSuccess(v -> + checkTransaction(hadTransaction)) + .subscriberContext(with(token)) + .block(); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test + public void testEmptyFluxOnComplete() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> { + Token token = createToken(); + Flux.empty() + .subscribeOn(Schedulers.elastic()) + .doOnComplete(() -> + checkTransaction(hadTransaction)) + .subscriberContext(with(token)) + .blockFirst(); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test + public void testMonoOnSuccess() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> { + Token token = createToken(); + Mono.just(A_VALUE) + .subscribeOn(Schedulers.elastic()) + .doOnSuccess(v -> + checkTransaction(hadTransaction)) + .subscriberContext(with(token)) + .block(); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test + public void testMonoRetryOnSuccess() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> { + Token token = createToken(); + AtomicBoolean firstCall = new AtomicBoolean(true); + Mono + .create(monoSink -> + inAnotherThread(() -> { + if (firstCall.getAndSet(false)) + monoSink.error(new RuntimeException("failing the first call")); + else + monoSink.success(A_VALUE); + }) + ) + .doOnSuccess(v -> + checkTransaction(hadTransaction)) + .retry(2) + .subscriberContext(with(token)) + .block(); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test + public void testMonoRetryBackoffOnSuccess() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> { + Token token = createToken(); + AtomicBoolean firstCall = new AtomicBoolean(true); + Mono + .create(monoSink -> + inAnotherThread(() -> { + if (firstCall.getAndSet(false)) + monoSink.error(new RuntimeException("failing the first call")); + else + monoSink.success(A_VALUE); + }) + ) + .doOnSuccess(v -> + checkTransaction(hadTransaction)) + .retryBackoff(2, Duration.ZERO) + .subscriberContext(with(token)) + .block(); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test + public void testMonoNestedInFlatMap() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> { + Token token = createToken(); + Mono.just(A_VALUE) + .subscribeOn(Schedulers.elastic()) + .flatMap(v -> + Mono.just(A_VALUE) + .subscribeOn(Schedulers.elastic()) + .doOnSuccess(v2 -> + checkTransaction(hadTransaction))) + .subscriberContext(with(token)) + .block(); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Trace(dispatcher = true) + public void inTransaction(Runnable actions) { + actions.run(); + } + + public void inAnotherThread(Runnable runnable) { + new Thread(runnable).start(); + } + + @Trace(async = true) + public void inAnnotatedWithTraceAsync(Runnable runnable) { + runnable.run(); + } + + public Token createToken() { + return AgentBridge.getAgent().getTransaction(false).getToken(); + } + + public Context with(Token token) { + return Context.empty().put("newrelic-token", token); + } + + @Trace + public void checkTransaction(AtomicBoolean hadTransaction) { + hadTransaction.set(AgentBridge.getAgent().getTransaction(false) != null); + } + + private void assertCapturedData(AtomicBoolean hadTransaction) { + assertTrue("Did not have transaction", hadTransaction.get()); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + + assertThat("No finished transactions", introspector.getFinishedTransactionCount(), + is(greaterThan(0))); + + assertThat("Transaction names", introspector.getTransactionNames(), contains( + "OtherTransaction/Custom/" + getClass().getName() + "/inTransaction" + )); + + assertThat("Unscoped metrics", introspector.getUnscopedMetrics().keySet(), hasItems( + "Java/" + getClass().getName() + "/inTransaction", + "Custom/" + getClass().getName() + "/checkTransaction" + )); + } +} diff --git a/instrumentation/netty-reactor-0.9.0/build.gradle b/instrumentation/netty-reactor-0.9.0/build.gradle new file mode 100644 index 0000000000..2189ccc502 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/build.gradle @@ -0,0 +1,19 @@ +dependencies { + implementation(project(":agent-bridge")) + implementation("io.projectreactor.netty:reactor-netty:0.9.0.RELEASE") +} + + + +jar { + manifest { attributes 'Implementation-Title': 'com.newrelic.instrumentation.netty-reactor-0.9.0' } +} + +verifyInstrumentation { + passesOnly 'io.projectreactor.netty:reactor-netty:[0.9.0.RELEASE,)' +} + +site { + title 'Netty Reactor' + type 'Appserver' +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java b/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java new file mode 100644 index 0000000000..07d671a6b2 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/NettyReactorConfig.java @@ -0,0 +1,18 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation; + +import com.newrelic.api.agent.NewRelic; + +public class NettyReactorConfig { + public static final boolean errorsEnabled = NewRelic.getAgent().getConfig() + .getValue("reactor-netty.errors.enabled", false); + + private NettyReactorConfig() { + } +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/SubscriptionWrapper.java b/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/SubscriptionWrapper.java new file mode 100644 index 0000000000..4d011a81f7 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/SubscriptionWrapper.java @@ -0,0 +1,37 @@ +/* + * + * * Copyright 2026 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.instrumentation; + +import com.newrelic.api.agent.Token; +import org.reactivestreams.Subscription; +import reactor.util.context.Context; + +public class SubscriptionWrapper implements Subscription { + + Subscription delegate; + Context currentContext; + + public SubscriptionWrapper(Subscription delegate, Context context) { + this.delegate = delegate; + this.currentContext = context; + } + + @Override + public void request(long n) { + delegate.request(n); + } + + @Override + public void cancel() { + Token token = currentContext.getOrDefault("newrelic-token", null); + if (token != null) { + token.linkAndExpire(); + } + if (delegate != null) delegate.cancel(); + } +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java b/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java new file mode 100644 index 0000000000..541977f380 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/com/nr/instrumentation/reactor/netty/TokenLinkingSubscriber.java @@ -0,0 +1,107 @@ +package com.nr.instrumentation.reactor.netty; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.nr.instrumentation.NettyReactorConfig; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; +import reactor.core.Fuseable; +import reactor.core.Scannable; +import reactor.core.publisher.Operators; +import reactor.util.context.Context; + +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * Implementation of a reactor.core.CoreSubscriber (a Context aware subscriber) that can be added as + * a lifecycle hook on Flux/Mono operators to propagate, retrieve, and link Tokens across async contexts. + * + * Based on OpenTelemetry code: + * https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/reactor-3.1/library/src/main/java/io/opentelemetry/instrumentation/reactor/TracingSubscriber.java + * @param + */ +public class TokenLinkingSubscriber implements CoreSubscriber { + private final Token token; + private final Subscriber subscriber; + private Context context; + + public TokenLinkingSubscriber(Subscriber subscriber, Context ctx) { + this.subscriber = subscriber; + this.context = ctx; + // newrelic-token is added by spring-webflux instrumentation of ServerWebExchange + this.token = ctx.getOrDefault("newrelic-token", null); + } + + @Override + public void onSubscribe(Subscription subscription) { + withNRToken(() -> subscriber.onSubscribe(subscription)); + } + + @Override + public void onNext(T o) { + withNRToken(() -> subscriber.onNext(o)); + } + + @Override + public void onError(Throwable throwable) { + withNRError(() -> subscriber.onError(throwable), throwable); + } + + @Override + public void onComplete() { + withNRToken(subscriber::onComplete); + } + + @Override + public Context currentContext() { + return context; + } + + @Trace(async = true, excludeFromTransactionTrace = true) + private void withNRToken(Runnable runnable) { + if (token != null && AgentBridge.getAgent().getTransaction(false) == null) { + token.link(); + } + runnable.run(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + private void withNRError(Runnable runnable, Throwable throwable) { + if (token != null && token.isActive()) { + token.link(); + if (NettyReactorConfig.errorsEnabled) { + NewRelic.noticeError(throwable); + } + } + runnable.run(); + } + + public static Function, ? extends Publisher> tokenLift() { + return Operators.lift(new TokenLifter<>()); + } + + private static class TokenLifter + implements BiFunction, CoreSubscriber> { + + public TokenLifter() { + } + + @Override + public CoreSubscriber apply(Scannable publisher, CoreSubscriber sub) { + // if Flux/Mono #just, #empty, #error + if (publisher instanceof Fuseable.ScalarCallable) { + return sub; + } + Token token = sub.currentContext().getOrDefault("newrelic-token", null); + if (token != null ) { + return new TokenLinkingSubscriber<>(sub, sub.currentContext()); + } + return sub; + } + } +} \ No newline at end of file diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/FluxMapFuseable_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/FluxMapFuseable_Instrumentation.java new file mode 100644 index 0000000000..b16534d7c9 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/FluxMapFuseable_Instrumentation.java @@ -0,0 +1,68 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.publisher; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.WeaveAllConstructors; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(type = MatchType.ExactClass, originalName = "reactor.core.publisher.FluxMapFuseable") +abstract class FluxMapFuseable_Instrumentation { + + @Weave(type = MatchType.ExactClass, originalName = "reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber") + static final class MapFuseableSubscriber_Instrumentation { + @NewField + private Token token; + + @WeaveAllConstructors + MapFuseableSubscriber_Instrumentation() { + if (AgentBridge.getAgent().getTransaction(false) != null && token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + public void onComplete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + public void onNext(T t) { + if (token != null) { + // not ideal to do this in onNext, but we're seeing situations where nothing else is ever called + // no onComplete, no onError, no cancel + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + public void onError(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + public void cancel() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + } +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java new file mode 100644 index 0000000000..b573bfefe9 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/Hooks_Instrumentation.java @@ -0,0 +1,25 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.publisher; + +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; + +import java.util.concurrent.atomic.AtomicBoolean; + +@Weave(originalName = "reactor.core.publisher.Hooks") +public abstract class Hooks_Instrumentation { + + /* + * Note that sub-hooks are cumulative. We want to avoid setting the same sub-hooks + * more than once, so we set this boolean to true the first time we set a sub-hook. + * if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { Hooks.onEachOperator(...) } + */ + @NewField + public static AtomicBoolean instrumented = new AtomicBoolean(false); +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaMonoSubscriber_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaMonoSubscriber_Instrumentation.java new file mode 100644 index 0000000000..23e90a78f7 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaMonoSubscriber_Instrumentation.java @@ -0,0 +1,78 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.publisher; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.WeaveAllConstructors; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.SubscriptionWrapper; +import org.reactivestreams.Subscription; +import reactor.util.context.Context; + +@Weave(originalName = "reactor.core.publisher.LambdaMonoSubscriber") +abstract class LambdaMonoSubscriber_Instrumentation { + @NewField + private Context nrContext; + final Context initialContext = Weaver.callOriginal(); + + @WeaveAllConstructors + protected LambdaMonoSubscriber_Instrumentation() { + // LamdaMonoSubscriber creates a new Context, so we create a new token and put it on the Context + // to be linked by TokenLinkingSubscriber but expired on onComplete here + if (AgentBridge.getAgent().getTransaction(false) != null + && initialContext.getOrDefault("newrelic-token", null) == null) { + nrContext = Context.of("newrelic-token", NewRelic.getAgent().getTransaction().getToken()); + } + } + + public final void onComplete() { + Token token = this.currentContext().getOrDefault("newrelic-token", null); + if (token != null) { + token.expire(); + this.nrContext = null; + } + Weaver.callOriginal(); + } + + public final void onError(Throwable t) { + Token token = this.currentContext().getOrDefault("newrelic-token", null); + if (token != null) { + token.expire(); + this.nrContext = null; + } + Weaver.callOriginal(); + } + + public void dispose() { + Token token = this.currentContext().getOrDefault("newrelic-token", null); + if (token != null) { + token.expire(); + this.nrContext = null; + } + Weaver.callOriginal(); + } + + public final void onSubscribe(Subscription s) { + if (s != null) { + s = new SubscriptionWrapper(s, this.currentContext()); + } + Weaver.callOriginal(); + } + + public Context currentContext() { + if (nrContext != null) { + return initialContext.putAll(nrContext); + } + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaSubscriber_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaSubscriber_Instrumentation.java new file mode 100644 index 0000000000..2708bbf9b2 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/LambdaSubscriber_Instrumentation.java @@ -0,0 +1,77 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.publisher; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.WeaveAllConstructors; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.SubscriptionWrapper; +import org.reactivestreams.Subscription; +import reactor.util.context.Context; + +@Weave(originalName = "reactor.core.publisher.LambdaSubscriber") +abstract class LambdaSubscriber_Instrumentation { + final Context initialContext = Weaver.callOriginal(); + @NewField + private Context nrContext; + + @WeaveAllConstructors + protected LambdaSubscriber_Instrumentation() { + if (AgentBridge.getAgent().getTransaction(false) != null + && initialContext.getOrDefault("newrelic-token", null) == null) { + nrContext = Context.of("newrelic-token", NewRelic.getAgent().getTransaction().getToken()); + } + } + + public final void onComplete() { + Token token = this.currentContext().getOrDefault("newrelic-token", null); + if (token != null) { + token.expire(); + this.nrContext = null; + } + Weaver.callOriginal(); + } + + public final void onError(Throwable t) { + Token token = this.currentContext().getOrDefault("newrelic-token", null); + if (token != null) { + token.expire(); + this.nrContext = null; + } + Weaver.callOriginal(); + } + + public void dispose() { + Token token = this.currentContext().getOrDefault("newrelic-token", null); + if (token != null) { + token.expire(); + this.nrContext = null; + } + Weaver.callOriginal(); + } + + public final void onSubscribe(Subscription s) { + if (s != null) { + s = new SubscriptionWrapper(s, this.currentContext()); + } + Weaver.callOriginal(); + } + + public Context currentContext() { + if (nrContext != null) { + //return nrContext; + return initialContext.putAll(nrContext); + } + return Weaver.callOriginal(); + } + +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/MonoSubscribeOn_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/MonoSubscribeOn_Instrumentation.java new file mode 100644 index 0000000000..40695bd9c6 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/publisher/MonoSubscribeOn_Instrumentation.java @@ -0,0 +1,73 @@ +/* + * + * * Copyright 2024 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.publisher; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.NewField; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.WeaveAllConstructors; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(type = MatchType.ExactClass, originalName = "reactor.core.publisher.MonoSubscribeOn") +abstract class MonoSubscribeOn_Instrumentation { + + @Weave(type = MatchType.ExactClass, originalName = "reactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber") + static final class SubscribeOnSubscriber_Instrumentation { + @NewField + private Token token; + + @WeaveAllConstructors + SubscribeOnSubscriber_Instrumentation() { + if (NewRelic.getAgent().getTransaction() != null && token == null) { + token = NewRelic.getAgent().getTransaction().getToken(); + } + } + + public void run () { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + public void onNext(T t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + public void onComplete() { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + public void onError(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + + public void cancel () { + if (token != null) { + token.linkAndExpire(); + token = null; + } + Weaver.callOriginal(); + } + } +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java new file mode 100644 index 0000000000..4095149fa9 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/InstantPeriodicWorkerTask_Instrumentation.java @@ -0,0 +1,23 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.scheduler; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.scheduler.InstantPeriodicWorkerTask") +final class InstantPeriodicWorkerTask_Instrumentation { + + // We need to be able to link the Token here when executing on a supplied Scheduler + // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator + @Trace(async = true, excludeFromTransactionTrace = true) + public Void call() { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java new file mode 100644 index 0000000000..988d33ce26 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/PeriodicWorkerTask_Instrumentation.java @@ -0,0 +1,23 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.scheduler; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.scheduler.PeriodicWorkerTask") +final class PeriodicWorkerTask_Instrumentation { + + // We need to be able to link the Token here when executing on a supplied Scheduler + // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator + @Trace(async = true, excludeFromTransactionTrace = true) + public Void call() { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java new file mode 100644 index 0000000000..fda06f032a --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/SchedulerTask_Instrumentation.java @@ -0,0 +1,23 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.scheduler; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.scheduler.SchedulerTask") +final class SchedulerTask_Instrumentation { + + // We need to be able to link the Token here when executing on a supplied Scheduler via Mono::publishOn + // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator + @Trace(async = true, excludeFromTransactionTrace = true) + public Void call() { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java new file mode 100644 index 0000000000..9bef1d7d91 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java @@ -0,0 +1,44 @@ +/* + * Copyright 2021 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package reactor.core.scheduler; + +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber; +import reactor.core.publisher.Hooks; +import reactor.core.publisher.Hooks_Instrumentation; + +import static com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber.tokenLift; + +@Weave(type = MatchType.BaseClass, originalName = "reactor.core.scheduler.Schedulers") +public abstract class Schedulers_Instrumentation { + + @Weave(type = MatchType.ExactClass, originalName = "reactor.core.scheduler.Schedulers$CachedScheduler") + static class CachedScheduler { + final Scheduler cached; + final String stringRepresentation; + + CachedScheduler(String key, Scheduler cached) { + /* + * Add tokenLift hook if it hasn't already been added. This allows for tokens to be retrieved from + * the current context and linked across threads at various points of the Flux/Mono lifecycle. + * + * When using Netty Reactor with SpringBoot this hook will be added by the HttpTrafficHandler_Instrumentation + * but when using other embedded web servers (e.g. Tomcat, Jetty, Undertow) the HttpTrafficHandler class + * doesn't get loaded and thus the hook isn't added. This ensures that the hook is added in a common code + * path before any Scheduler Tasks are spun off on new threads. + */ + if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { + Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); + } + + this.cached = Weaver.callOriginal(); + this.stringRepresentation = Weaver.callOriginal(); + } + } + +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java new file mode 100644 index 0000000000..76867aca47 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/core/scheduler/WorkerTask_Instrumentation.java @@ -0,0 +1,23 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.core.scheduler; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; + +@Weave(originalName = "reactor.core.scheduler.WorkerTask") +final class WorkerTask_Instrumentation { + + // We need to be able to link the Token here when executing on a supplied Scheduler + // A Token should be available on the thread that this task executes on if tokenLift() was added to Hooks.onEachOperator + @Trace(async = true, excludeFromTransactionTrace = true) + public Void call() { + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java new file mode 100644 index 0000000000..5eb65e3d1d --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/client/HttpClientConnect_Instrumentation.java @@ -0,0 +1,36 @@ +/* + * + * * Copyright 2022 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package reactor.netty.http.client; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import reactor.netty.Connection; +import reactor.util.context.Context; + +@Weave(originalName = "reactor.netty.http.client.HttpClientConnect") +final class HttpClientConnect_Instrumentation { + + @Weave(originalName = "reactor.netty.http.client.HttpClientConnect$HttpObserver") + static final class HttpObserver_Instrumentation { + + public Context currentContext() { + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public void onUncaughtException(Connection connection, Throwable error) { + Context ctx = currentContext(); + Token token = ctx != null ? ctx.getOrDefault("newrelic-token", null) : null; + if (token != null && token.isActive()) { + token.link(); + } + Weaver.callOriginal(); + } + } +} diff --git a/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java new file mode 100644 index 0000000000..d998d6d2e4 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Instrumentation.java @@ -0,0 +1,35 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.netty.http.server; + +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber; +import io.netty.channel.ChannelHandlerContext; +import reactor.core.publisher.Hooks; +import reactor.core.publisher.Hooks_Instrumentation; + +import static com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber.tokenLift; + +@Weave(originalName = "reactor.netty.http.server.HttpTrafficHandler") +class HttpTrafficHandler_Instrumentation { + public void channelRead(ChannelHandlerContext ctx, Object msg) { + + /* + * Add tokenLift hook if it hasn't already been added. This allows for tokens to be retrieved from + * the current context and linked across threads at various points of the Flux/Mono lifecycle. + * + * This hook will only be added when using Netty Reactor with SpringBoot. When using other embedded web + * servers (e.g. Tomcat, Jetty, Undertow) the Schedulers_Instrumentation class will add the hook. + */ + if (!Hooks_Instrumentation.instrumented.getAndSet(true)) { + Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/netty-reactor-0.9.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java b/instrumentation/netty-reactor-0.9.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java new file mode 100644 index 0000000000..985994ae09 --- /dev/null +++ b/instrumentation/netty-reactor-0.9.0/src/test/java/com/nr/agent/instrumentation/TransactionPropagationTest.java @@ -0,0 +1,351 @@ +package com.nr.agent.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Token; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.api.agent.Trace; +import com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Hooks; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; +import reactor.util.context.Context; + +import java.time.Duration; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; + +import static com.nr.instrumentation.reactor.netty.TokenLinkingSubscriber.tokenLift; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertTrue; + +@SuppressWarnings("deprecation") +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = {"reactor"}) +public class TransactionPropagationTest { + + public static final String A_VALUE = ""; + + @BeforeClass + public static void init() { + Hooks.onEachOperator(TokenLinkingSubscriber.class.getName(), tokenLift()); + } + + @Test + public void syncPropagationSanityCheck() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> + checkTransaction(hadTransaction)); + assertCapturedData(hadTransaction); + } + + @Test + public void asyncPropagationSanityCheck() throws InterruptedException { + AtomicBoolean hadTransaction = new AtomicBoolean(); + CountDownLatch done = new CountDownLatch(1); + inTransaction(() -> { + Token token = createToken(); + inAnotherThread(() -> + inAnnotatedWithTraceAsync(() -> { + token.linkAndExpire(); + checkTransaction(hadTransaction); + done.countDown(); + }) + ); + }); + done.await(); + assertCapturedData(hadTransaction); + } + + @Test + public void testReactorSchedulersInstrumentation() throws InterruptedException { + AtomicBoolean hadTransaction = new AtomicBoolean(); + CountDownLatch done = new CountDownLatch(1); + inTransaction(() -> { + Token token = createToken(); + Schedulers.elastic().schedule(() -> { +// trace_async(() -> { it is not need as Tasks are instrumented and annotated with @Trace(async = ture) + token.linkAndExpire(); + checkTransaction(hadTransaction); + done.countDown(); +// }); + }); + }); + done.await(); + assertCapturedData(hadTransaction); + } + + @Test + public void testEmptyMonoOnSuccess() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> { + Token token = createToken(); + Mono.empty() + .subscribeOn(Schedulers.elastic()) + .doOnSuccess(v -> + checkTransaction(hadTransaction)) + .subscriberContext(with(token)) + .block(); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test + public void testEmptyFluxOnComplete() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> { + Token token = createToken(); + Flux.empty() + .subscribeOn(Schedulers.elastic()) + .doOnComplete(() -> + checkTransaction(hadTransaction)) + .subscriberContext(with(token)) + .blockFirst(); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test + public void testMonoOnSuccess() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> { + Token token = createToken(); + Mono.just(A_VALUE) + .subscribeOn(Schedulers.elastic()) + .doOnSuccess(v -> + checkTransaction(hadTransaction)) + .subscriberContext(with(token)) + .block(); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test + public void testMonoRetryOnSuccess() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> { + Token token = createToken(); + AtomicBoolean firstCall = new AtomicBoolean(true); + Mono + .create(monoSink -> + inAnotherThread(() -> { + if (firstCall.getAndSet(false)) + monoSink.error(new RuntimeException("failing the first call")); + else + monoSink.success(A_VALUE); + }) + ) + .doOnSuccess(v -> + checkTransaction(hadTransaction)) + .retry(2) + .subscriberContext(with(token)) + .block(); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test + public void testMonoRetryBackoffOnSuccess() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> { + Token token = createToken(); + AtomicBoolean firstCall = new AtomicBoolean(true); + Mono + .create(monoSink -> + inAnotherThread(() -> { + if (firstCall.getAndSet(false)) + monoSink.error(new RuntimeException("failing the first call")); + else + monoSink.success(A_VALUE); + }) + ) + .doOnSuccess(v -> + checkTransaction(hadTransaction)) + .retryBackoff(2, Duration.ZERO) + .subscriberContext(with(token)) + .block(); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test + public void testMonoNestedInFlatMap() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + inTransaction(() -> { + Token token = createToken(); + Mono.just(A_VALUE) + .subscribeOn(Schedulers.elastic()) + .flatMap(v -> + Mono.just(A_VALUE) + .subscribeOn(Schedulers.elastic()) + .doOnSuccess(v2 -> + checkTransaction(hadTransaction))) + .subscriberContext(with(token)) + .block(); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test(timeout = 10000L) + public void testLambdaMonoSubscriberOnSuccess() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + CountDownLatch done = new CountDownLatch(1); + inTransaction(() -> { + Token token = createToken(); + Mono.empty() + .subscribeOn(Schedulers.elastic()) + .doOnSuccess(v -> + checkTransaction(hadTransaction)) + + // it is not need as LambdaMonoSubscriber instrumentation creates token + // and puts it into the context + //.subscriberContext(with(token)) + + // Call countDown in onComplete to see that instrumentation code calls original method + .subscribe(nil(), nil(), done::countDown); + await(done); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test(timeout = 10000L) + public void testLambdaMonoSubscriberOnError() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + CountDownLatch done = new CountDownLatch(1); + inTransaction(() -> { + Token token = createToken(); + Mono.error(new RuntimeException()) + .subscribeOn(Schedulers.elastic()) + .doOnError(v -> + checkTransaction(hadTransaction)) + + // it is not need as LambdaMonoSubscriber instrumentation creates token + // and puts it into the context + //.subscriberContext(with(token)) + + // Call countDown in onError to see that instrumentation code calls original method + .subscribe(nil(), v -> done.countDown()); + await(done); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test(timeout = 10000L) + public void testLambdaSubscriberOnComplete() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + CountDownLatch done = new CountDownLatch(1); + inTransaction(() -> { + Token token = createToken(); + Flux.empty() + .subscribeOn(Schedulers.elastic()) + .doOnComplete(() -> + checkTransaction(hadTransaction)) + + // it is not need as LambdaSubscriber instrumentation creates token + // and puts it into the context + //.subscriberContext(with(token)) + + // Call countDown in onComplete to see that instrumentation code calls original method + .subscribe(nil(), nil(), done::countDown); + await(done); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Test(timeout = 10000L) + public void testLambdaSubscriberOnError() { + AtomicBoolean hadTransaction = new AtomicBoolean(); + CountDownLatch done = new CountDownLatch(1); + inTransaction(() -> { + Token token = createToken(); + Flux.error(new RuntimeException()) + .subscribeOn(Schedulers.elastic()) + .doOnError(v -> + checkTransaction(hadTransaction)) + + // it is not need as LambdaSubscriber instrumentation creates token + // and puts it into the context + //.subscriberContext(with(token)) + + // Call countDown in onError to see that instrumentation code calls original method + .subscribe(nil(), v -> done.countDown()); + await(done); + token.expire(); + }); + assertCapturedData(hadTransaction); + } + + @Trace(dispatcher = true) + public void inTransaction(Runnable actions) { + actions.run(); + } + + public void inAnotherThread(Runnable runnable) { + new Thread(runnable).start(); + } + + @Trace(async = true) + public void inAnnotatedWithTraceAsync(Runnable runnable) { + runnable.run(); + } + + public Token createToken() { + return AgentBridge.getAgent().getTransaction(false).getToken(); + } + + public Context with(Token token) { + return Context.empty().put("newrelic-token", token); + } + + @Trace + public void checkTransaction(AtomicBoolean hadTransaction) { + hadTransaction.set(AgentBridge.getAgent().getTransaction(false) != null); + } + + private Consumer nil() { + return v -> { + }; + } + + private void await(CountDownLatch done) { + try { + done.await(); + } catch (InterruptedException ignore) { + } + } + + private void assertCapturedData(AtomicBoolean hadTransaction) { + assertTrue("Did not have transaction", hadTransaction.get()); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + + assertThat("No finished transactions", introspector.getFinishedTransactionCount(), + is(greaterThan(0))); + + assertThat("Transaction names", introspector.getTransactionNames(), contains( + "OtherTransaction/Custom/" + getClass().getName() + "/inTransaction" + )); + + assertThat("Unscoped metrics", introspector.getUnscopedMetrics().keySet(), hasItems( + "Java/" + getClass().getName() + "/inTransaction", + "Custom/" + getClass().getName() + "/checkTransaction" + )); + } +} diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java new file mode 100644 index 0000000000..4831e7c131 --- /dev/null +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java @@ -0,0 +1,15 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.netty.http.server; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +// prevents this module from applying when reactor-netty 0.8.0 is present +@SkipIfPresent(originalName = "reactor.netty.http.server.HttpTrafficHandler") +class HttpTrafficHandler_Skip { +} diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java new file mode 100644 index 0000000000..4831e7c131 --- /dev/null +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java @@ -0,0 +1,15 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.netty.http.server; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +// prevents this module from applying when reactor-netty 0.8.0 is present +@SkipIfPresent(originalName = "reactor.netty.http.server.HttpTrafficHandler") +class HttpTrafficHandler_Skip { +} diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java new file mode 100644 index 0000000000..4831e7c131 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java @@ -0,0 +1,15 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.netty.http.server; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +// prevents this module from applying when reactor-netty 0.8.0 is present +@SkipIfPresent(originalName = "reactor.netty.http.server.HttpTrafficHandler") +class HttpTrafficHandler_Skip { +} diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java new file mode 100644 index 0000000000..4831e7c131 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java @@ -0,0 +1,15 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.netty.http.server; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +// prevents this module from applying when reactor-netty 0.8.0 is present +@SkipIfPresent(originalName = "reactor.netty.http.server.HttpTrafficHandler") +class HttpTrafficHandler_Skip { +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java new file mode 100644 index 0000000000..4831e7c131 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/netty/http/server/HttpTrafficHandler_Skip.java @@ -0,0 +1,15 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package reactor.netty.http.server; + +import com.newrelic.api.agent.weaver.SkipIfPresent; + +// prevents this module from applying when reactor-netty 0.8.0 is present +@SkipIfPresent(originalName = "reactor.netty.http.server.HttpTrafficHandler") +class HttpTrafficHandler_Skip { +} diff --git a/settings.gradle b/settings.gradle index a9476a5c76..8067d6dd3b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -315,6 +315,8 @@ include 'instrumentation:netty-4.0.0' include 'instrumentation:netty-4.0.8' include 'instrumentation:netty-4.1.16' include 'instrumentation:netty-reactor-0.7.0' +include 'instrumentation:netty-reactor-0.8.0' +include 'instrumentation:netty-reactor-0.9.0' include 'instrumentation:okhttp-3.6.0' include 'instrumentation:okhttp-3.14.0' include 'instrumentation:okhttp-4.0.0' From d25125caaa6fa7aa9cca88a24c88c92b38fd432d Mon Sep 17 00:00:00 2001 From: Doug Hilpipre Date: Thu, 9 Apr 2026 15:41:22 -0500 Subject: [PATCH 6/7] removed unneeded tokens in version 3.5 --- .../EmitterProcessor_Instrumentation.java | 24 ++---------- .../NextProcessor_Instrumentation.java | 17 +-------- .../ReplayProcessor_Instrumentation.java | 24 ++---------- .../SinkEmptyMulticast_Instrumentation.java | 21 +--------- .../SinkManyBestEffort_Instrumentation.java | 24 ++---------- .../SinkManySerialized_Instrumentation.java | 24 ++---------- .../SinkOneMulticast_Instrumentation.java | 4 -- .../UnicastProcessor_Instrumentation.java | 38 ++----------------- 8 files changed, 19 insertions(+), 157 deletions(-) diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java index 2d72a90ba3..79675a97e3 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -11,36 +11,18 @@ @Weave(originalName = "reactor.core.publisher.EmitterProcessor") public class EmitterProcessor_Instrumentation { - @NewField - private Token token; - - EmitterProcessor_Instrumentation(boolean autoCancel, int prefetch) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java index 62b35e2f4d..513b9e22a1 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java @@ -12,26 +12,13 @@ @Weave(originalName = "reactor.core.publisher.NextProcessor") class NextProcessor_Instrumentation { - @NewField - private Token token; - - NextProcessor_Instrumentation(CorePublisher source) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace Sinks.EmitResult tryEmitError(Throwable cause) { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace Sinks.EmitResult tryEmitValue(O value) { - token.linkAndExpire(); - token = null; return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java index f4e9d6fd9c..207e77ce97 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java @@ -11,39 +11,21 @@ @Weave(originalName = "reactor.core.publisher.ReplayProcessor") public class ReplayProcessor_Instrumentation { - @NewField - private Token token; - - ReplayProcessor_Instrumentation(FluxReplay.ReplayBuffer buffer) { - this.token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java index 1fde592f28..8638ee5741 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java @@ -11,33 +11,16 @@ @Weave(originalName = "reactor.core.publisher.SinkEmptyMulticast") class SinkEmptyMulticast_Instrumentation { - @NewField - protected Token token = null; - - SinkEmptyMulticast_Instrumentation() { - if(token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - @Trace(async=true) + @Trace public Sinks.EmitResult tryEmitEmpty() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async=true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java index c8db0cefe9..3870e2beba 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java @@ -11,39 +11,21 @@ @Weave(originalName = "reactor.core.publisher.SinkManyBestEffort") class SinkManyBestEffort_Instrumentation { - @NewField - private Token token; - - SinkManyBestEffort_Instrumentation(boolean allOrNothing) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java index bb50447e8e..bad42bfe41 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java @@ -11,39 +11,21 @@ @Weave(originalName = "reactor.core.publisher.SinkManySerialized") class SinkManySerialized_Instrumentation { - @NewField - private Token token; - - SinkManySerialized_Instrumentation(Sinks.Many sink, ContextHolder contextHolder) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); } - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java index f58b581bb8..3e4fb3e281 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java @@ -9,10 +9,6 @@ class SinkOneMulticast_Instrumentation extends SinkEmptyMulticast_Instrumenta @Trace(async=true) public Sinks.EmitResult tryEmitValue(O value) { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java index 31bfdea1a3..d5ac8f3c04 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -15,50 +15,18 @@ @Weave(originalName = "reactor.core.publisher.UnicastProcessor") public class UnicastProcessor_Instrumentation { - @NewField - private Token token; - - public UnicastProcessor_Instrumentation(Queue queue) { - if(token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - public UnicastProcessor_Instrumentation(Queue queue, Disposable onTerminate) { - if(token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - public UnicastProcessor_Instrumentation(Queue queue, Consumer onOverflow, Disposable onTerminate) { - if(token == null) { - token = NewRelic.getAgent().getTransaction().getToken(); - } - } - - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitComplete() { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitError(Throwable t) { - if(token != null) { - token.linkAndExpire(); - token = null; - } return Weaver.callOriginal(); } - @Trace(async = true) + @Trace public Sinks.EmitResult tryEmitNext(T t) { - if(token != null) { - token.link(); - } return Weaver.callOriginal(); } From b726c008db6942634231120bc4182988b11d6864 Mon Sep 17 00:00:00 2001 From: Doug Hilpipre Date: Fri, 10 Apr 2026 14:59:00 -0500 Subject: [PATCH 7/7] added excludeFromTransactionTrace flags on most @Trace, finished adding tests --- .../reactor/NRRunnableWrapper.java | 2 +- .../publisher/FluxCreate_Instrumentation.java | 12 +- .../publisher/MonoCreate_Instrumentation.java | 8 +- .../reactor/NRRunnableWrapper.java | 2 +- .../publisher/FluxCreate_Instrumentation.java | 24 +- .../publisher/MonoCreate_Instrumentation.java | 8 +- .../reactor/test/TestApplication.java | 30 -- .../reactor/NRRunnableWrapper.java | 2 +- .../EmitterProcessor_Instrumentation.java | 6 +- .../publisher/FluxCreate_Instrumentation.java | 24 +- .../publisher/MonoCreate_Instrumentation.java | 10 +- .../NextProcessor_Instrumentation.java | 6 +- .../ReplayProcessor_Instrumentation.java | 6 +- .../SinkEmptyMulticast_Instrumentation.java | 3 +- .../SinkEmptySerialized_Instrumentation.java | 3 +- .../SinkManyBestEffort_Instrumentation.java | 3 +- .../SinkManySerialized_Instrumentation.java | 5 +- .../SinkOneSerialized_Instrumentation.java | 2 +- ...anySinkNoBackpressure_Instrumentation.java | 5 +- .../UnicastProcessor_Instrumentation.java | 5 +- .../scheduler/Schedulers_Instrumentation.java | 1 - .../reactor/test/MonoCoreSubscriber.java | 33 ++ .../reactor/test/SubscriptionConsumer.java | 23 + .../reactor/test/TestApplication.java | 299 ++++++++++-- .../reactor/NRRunnableWrapper.java | 2 +- .../EmitterProcessor_Instrumentation.java | 5 +- .../publisher/FluxCreate_Instrumentation.java | 24 +- .../publisher/MonoCreate_Instrumentation.java | 10 +- .../NextProcessor_Instrumentation.java | 4 +- .../ReplayProcessor_Instrumentation.java | 5 +- .../SinkEmptyMulticast_Instrumentation.java | 3 +- .../SinkEmptySerialized_Instrumentation.java | 3 +- .../SinkManyBestEffort_Instrumentation.java | 5 +- .../SinkManySerialized_Instrumentation.java | 5 +- .../SinkOneMulticast_Instrumentation.java | 2 +- .../SinkOneSerialized_Instrumentation.java | 2 +- ...anySinkNoBackpressure_Instrumentation.java | 5 +- .../UnicastProcessor_Instrumentation.java | 5 +- .../reactor/test/AwaitMany.java | 37 ++ .../reactor/test/AwaitSingle.java | 32 ++ .../reactor/test/MonoCoreSubscriber.java | 33 ++ .../reactor/test/SubscriptionConsumer.java | 23 + .../reactor/test/TestApplication.java | 414 +++++++++++++++++ .../reactor/test/TestFluxCoreSubscriber.java | 52 +++ .../reactor/test/TestMonoCoreSubscriber.java | 50 ++ .../reactor/NRRunnableWrapper.java | 2 +- .../EmitterProcessor_Instrumentation.java | 8 +- .../publisher/FluxCreate_Instrumentation.java | 24 +- .../publisher/MonoCreate_Instrumentation.java | 11 +- .../NextProcessor_Instrumentation.java | 7 +- .../ReplayProcessor_Instrumentation.java | 7 +- .../SinkEmptyMulticast_Instrumentation.java | 5 +- .../SinkEmptySerialized_Instrumentation.java | 5 +- .../SinkManyBestEffort_Instrumentation.java | 7 +- .../SinkManySerialized_Instrumentation.java | 7 +- .../SinkOneMulticast_Instrumentation.java | 2 +- .../SinkOneSerialized_Instrumentation.java | 2 +- .../UnicastProcessor_Instrumentation.java | 12 +- .../reactor/test/AwaitMany.java | 37 ++ .../reactor/test/AwaitSingle.java | 32 ++ .../reactor/test/MonoCoreSubscriber.java | 33 ++ .../reactor/test/SubscriptionConsumer.java | 23 + .../reactor/test/TestApplication.java | 426 ++++++++++++++++++ .../reactor/test/TestFluxCoreSubscriber.java | 52 +++ .../reactor/test/TestMonoCoreSubscriber.java | 50 ++ 65 files changed, 1759 insertions(+), 236 deletions(-) create mode 100644 instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java create mode 100644 instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java create mode 100644 instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java create mode 100644 instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java create mode 100644 instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java create mode 100644 instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java create mode 100644 instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java create mode 100644 instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java create mode 100644 instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java create mode 100644 instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java create mode 100644 instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java b/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java index ffb4a47ac8..6fbcb1db51 100644 --- a/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java +++ b/instrumentation/reactor-core-3.1.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java @@ -21,7 +21,7 @@ public NRRunnableWrapper(Runnable r, Token t) { } @Override - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void run() { if(token != null) { token.linkAndExpire(); diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java index e763d2a24f..d7e22e025f 100644 --- a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -33,7 +33,7 @@ static abstract class BaseSink_Instrumentation implements FluxSink { } } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void complete() { if (token != null) { token.linkAndExpire(); @@ -42,7 +42,7 @@ public void complete() { Weaver.callOriginal(); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void error(Throwable e) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(e); @@ -62,7 +62,7 @@ static abstract class BufferAsyncSink_Instrumentation extends BaseSink_Instru super(actual); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if(token != null) { token.link(); @@ -77,7 +77,7 @@ static abstract class IgnoreSink_Instrumentation extends BaseSink_Instrumenta super(actual); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if(token != null) { token.link(); @@ -92,7 +92,7 @@ static abstract class LatestAsyncSink_Instrumentation extends BaseSink_Instru super(actual); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if(token != null) { token.link(); @@ -107,7 +107,7 @@ static abstract class NoOverflowBaseAsyncSink_Instrumentation extends BaseSin super(actual); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if(token != null) { token.link(); diff --git a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java index 65c00b64b1..fcc76ee126 100644 --- a/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.1.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -28,7 +28,7 @@ static class DefaultMonoSink_Instrumentation { } } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void success() { if(token != null) { token.linkAndExpire(); @@ -37,7 +37,7 @@ public void success() { Weaver.callOriginal(); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void success(T value) { if(token != null) { token.linkAndExpire(); @@ -46,7 +46,7 @@ public void success(T value) { Weaver.callOriginal(); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void error(Throwable e) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(e); @@ -58,7 +58,7 @@ public void error(Throwable e) { Weaver.callOriginal(); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void cancel() { if(token != null) { token.linkAndExpire(); diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java b/instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java index ffb4a47ac8..6fbcb1db51 100644 --- a/instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java +++ b/instrumentation/reactor-core-3.3.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java @@ -21,7 +21,7 @@ public NRRunnableWrapper(Runnable r, Token t) { } @Override - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void run() { if(token != null) { token.linkAndExpire(); diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java index 215fa23125..d5ba761c06 100644 --- a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -33,7 +33,7 @@ static abstract class BaseSink_Instrumentation implements FluxSink { } } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void complete() { if (token != null) { @@ -43,7 +43,7 @@ public void complete() { Weaver.callOriginal(); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void error(Throwable e) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(e); @@ -63,7 +63,7 @@ static abstract class BufferAsyncSink_Instrumentation extends BaseSink_Instru super(actual); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if(token != null) { token.link(); @@ -78,7 +78,7 @@ static abstract class IgnoreSink_Instrumentation extends BaseSink_Instrumenta super(actual); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if(token != null) { token.link(); @@ -93,7 +93,7 @@ static abstract class LatestAsyncSink_Instrumentation extends BaseSink_Instru super(actual); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if(token != null) { token.link(); @@ -108,7 +108,7 @@ static abstract class NoOverflowBaseAsyncSink_Instrumentation extends BaseSin super(actual); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if(token != null) { token.link(); @@ -129,7 +129,7 @@ static class SerializeOnRequestSink_Instrumentation { } } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void complete() { if (token != null) { token.linkAndExpire(); @@ -138,7 +138,7 @@ public void complete() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable t) { if (token != null) { token.linkAndExpire(); @@ -147,7 +147,7 @@ public void error(Throwable t) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); @@ -169,7 +169,7 @@ static class SerializedSink_Instrumentation { } } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void complete() { if (token != null) { token.linkAndExpire(); @@ -178,7 +178,7 @@ public void complete() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable t) { if (token != null) { token.linkAndExpire(); @@ -187,7 +187,7 @@ public void error(Throwable t) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); diff --git a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java index 5e36e8e739..ac7f41a5bc 100644 --- a/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.3.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -29,7 +29,7 @@ static class DefaultMonoSink_Instrumentation { } } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void success() { if(token != null) { token.linkAndExpire(); @@ -38,7 +38,7 @@ public void success() { Weaver.callOriginal(); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void success(T value) { if(token != null) { token.linkAndExpire(); @@ -47,7 +47,7 @@ public void success(T value) { Weaver.callOriginal(); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void error(Throwable e) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(e); @@ -59,7 +59,7 @@ public void error(Throwable e) { Weaver.callOriginal(); } - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void cancel() { if(token != null) { token.linkAndExpire(); diff --git a/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java index c27a6db311..2000a67242 100644 --- a/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java +++ b/instrumentation/reactor-core-3.3.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java @@ -132,36 +132,6 @@ public void doMonoPublishOnTest() { } -// private void printSegments(TraceSegment segment, int indents) { -// String segmentName = segment.getName(); -// String classname = segment.getClassName(); -// String methodName = segment.getMethodName(); -// int callCount = segment.getCallCount(); -// StringBuilder sb = new StringBuilder(); -// for(int i = 0; i < indents; i++) { -// sb.append(" "); -// } -// sb.append("Name: ").append(segmentName); -// sb.append(", Class: ").append(classname); -// sb.append(", Method: ").append(methodName); -// sb.append(", CallCount: ").append(callCount); -// Map attributes = segment.getTracerAttributes(); -// if(attributes != null) { -// Set keys = attributes.keySet(); -// for(String key : keys) { -// Object value = attributes.get(key); -// sb.append(", Attribute: ").append(key); -// sb.append(": "); -// sb.append(value); -// } -// } -// System.out.println(sb.toString()); -// List children = segment.getChildren(); -// for(TraceSegment child : children) { -// printSegments(child, indents + 2); -// } -// } - @Test public void doFluxPublishOnTest() { testFluxPub(); diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java b/instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java index ffb4a47ac8..6fbcb1db51 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java @@ -21,7 +21,7 @@ public NRRunnableWrapper(Runnable r, Token t) { } @Override - @Trace(async=true) + @Trace(async=true, excludeFromTransactionTrace = true) public void run() { if(token != null) { token.linkAndExpire(); diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java index e986ec6194..127a9b224f 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -9,17 +9,17 @@ @Weave(originalName = "reactor.core.publisher.EmitterProcessor") public class EmitterProcessor_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java index a3e3ebff32..e47e9d4302 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -23,7 +23,7 @@ static abstract class BaseSink_Instrumentation { token = NewRelic.getAgent().getTransaction().getToken(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void complete() { if (token != null) { token.linkAndExpire(); @@ -32,7 +32,7 @@ public void complete() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable e) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(e); @@ -52,7 +52,7 @@ public void cancel() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void request(long n) { if (token != null) { token.link(); @@ -69,7 +69,7 @@ static final class BufferAsyncSink_Instrumentation extends BaseSink_Instrumen super(actual); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); @@ -86,7 +86,7 @@ static final class IgnoreSink_Instrumentation extends BaseSink_Instrumentatio super(actual); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); @@ -102,7 +102,7 @@ static abstract class NoOverflowBaseAsyncSink_Instrumentation extends BaseSin super(actual); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); @@ -125,7 +125,7 @@ static final class FluxCreate$SerializedFluxSink_Instrumentation { } } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void complete() { if (token != null) { token.linkAndExpire(); @@ -134,7 +134,7 @@ public void complete() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable t) { if (token != null) { token.linkAndExpire(); @@ -143,7 +143,7 @@ public void error(Throwable t) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); @@ -166,7 +166,7 @@ static class SerializeOnRequestSink_Instrumentation { } } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void complete() { if (token != null) { token.linkAndExpire(); @@ -175,7 +175,7 @@ public void complete() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable t) { if (token != null) { token.linkAndExpire(); @@ -184,7 +184,7 @@ public void error(Throwable t) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java index 11df91ef56..12620031a7 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -22,7 +22,7 @@ static final class DefaultMonoSink_Instrumentation { this.token = NewRelic.getAgent().getTransaction().getToken(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void cancel() { if(token != null) { token.linkAndExpire(); @@ -31,7 +31,7 @@ public void cancel() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable e) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(e); @@ -43,7 +43,7 @@ public void error(Throwable e) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void success() { if(token != null) { token.linkAndExpire(); @@ -52,7 +52,7 @@ public void success() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void success(T value) { if(token != null) { token.linkAndExpire(); @@ -61,7 +61,7 @@ public void success(T value) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void request(long n) { if(token != null) { token.link(); diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java index 56a0cfc855..b5787fc50a 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java @@ -9,17 +9,17 @@ @Weave(originalName = "reactor.core.publisher.NextProcessor") class NextProcessor_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitError(Throwable cause) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitValue(O value) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitEmpty() { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java index 2191f9da4b..1b689b3146 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java @@ -9,12 +9,12 @@ @Weave(originalName = "reactor.core.publisher.ReplayProcessor") public class ReplayProcessor_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -22,7 +22,7 @@ public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java index ac01283c87..6a08756ab5 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java @@ -9,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.SinkEmptyMulticast") class SinkEmptyMulticast_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitEmpty() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java index 2606e489a0..346d6cc8da 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java @@ -10,12 +10,11 @@ @Weave(originalName = "reactor.core.publisher.SinkEmptySerialized", type = MatchType.BaseClass) class SinkEmptySerialized_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitEmpty() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java index 6511007406..903c0f5643 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java @@ -9,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.SinkManyBestEffort") class SinkManyBestEffort_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java index 5ebe66f0de..81d384b6f4 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java @@ -9,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.SinkManySerialized") class SinkManySerialized_Instrumentation { - @Trace(async = true) + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace(async = true) public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -22,7 +21,7 @@ public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace(async = true) + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java index 92d8a5dc50..57a8af8c97 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java @@ -7,7 +7,7 @@ @Weave(originalName = "reactor.core.publisher.SinkOneSerialized") public abstract class SinkOneSerialized_Instrumentation extends SinkEmptySerialized_Instrumentation{ - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitValue(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java index 4c46de1eae..4f10a4d35f 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java @@ -9,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.UnicastManySinkNoBackpressure") class UnicastManySinkNoBackpressure_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -22,7 +21,7 @@ public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java index 780823a364..868972dd80 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -9,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.UnicastProcessor") public class UnicastProcessor_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -22,7 +21,7 @@ public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java index 2a4b38ae54..7f19cfb2aa 100644 --- a/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.0/src/main/java/reactor/core/scheduler/Schedulers_Instrumentation.java @@ -10,7 +10,6 @@ import com.nr.instrumentation.reactor.ReactorUtils; import reactor.core.Disposable; -import reactor.util.annotation.Nullable; @Weave(originalName = "reactor.core.scheduler.Schedulers") public class Schedulers_Instrumentation { diff --git a/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java new file mode 100644 index 0000000000..acca8cd6f8 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java @@ -0,0 +1,33 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; + +public class MonoCoreSubscriber implements CoreSubscriber { + + @Override + @Trace + public void onNext(String t) { + System.out.println("Received string for onNext: " + t); + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Received error for onError: " + t); + } + + @Override + @Trace + public void onComplete() { + System.out.println("Mono has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription var1) { + System.out.println("Mono was subscribed to by : " + var1); + } + +} diff --git a/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java new file mode 100644 index 0000000000..b3a2dce717 --- /dev/null +++ b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java @@ -0,0 +1,23 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; + +import java.util.function.Consumer; + +public class SubscriptionConsumer implements Consumer { + + @Override + @Trace + public void accept(Subscription subscription) { + pause(); + + } + + private void pause() { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + } +} diff --git a/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java index e2cbd80950..0f936b5dcd 100644 --- a/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java +++ b/instrumentation/reactor-core-3.4.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java @@ -11,10 +11,7 @@ import reactor.core.scheduler.Scheduler; import reactor.core.scheduler.Schedulers; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.*; @RunWith(InstrumentationTestRunner.class) @@ -30,6 +27,12 @@ public class TestApplication { private static final String SUBSCRIBER_NEXT = "Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext"; private static final String SERIALIZED_COMPLETE = "Java/reactor.core.publisher.SinkManySerialized/tryEmitComplete"; private static final String BEST_EFFORT_COMPLETE = "Java/reactor.core.publisher.SinkManyBestEffort/tryEmitComplete"; + private static final String txn1 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testMonoSub"; + private static final String txn2 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testMonoPub"; + private static final String txn3 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testFluxSub"; + private static final String txn4 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testFluxPub"; + private static final String WRAPPER = "Java/com.nr.instrumentation.reactor.NRRunnableWrapper/run"; + private static final String[] fluxArray = {"Message 1", "Message 2", "Message 3", "Message 4"}; @Test public void doSinkOneTest() { @@ -134,34 +137,276 @@ public void testSinkMany() { System.out.println("Await Many result : " + results); } - private void printSegments(TraceSegment segment, int indents) { - String segmentName = segment.getName(); - String classname = segment.getClassName(); - String methodName = segment.getMethodName(); - int callCount = segment.getCallCount(); - StringBuilder sb = new StringBuilder(); - for(int i = 0; i < indents; i++) { - sb.append(" "); + @Test + public void doMonoSubscribeOnTest() { + testMonoSub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transactions", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn1); // & txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn1); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + Collection traces = introspector.getTransactionTracesForTransaction(txn1); + System.out.println("Transaction traces: " + traces.size()); + for (TransactionTrace transactionTrace : traces) { + TraceSegment initialSegment = transactionTrace.getInitialTraceSegment(); + List children = initialSegment.getChildren(); + boolean passes = false; + /* + Assure that the subscribing occurs on another thread + */ + for (TraceSegment child : children) { + if (child.getName().equals(WRAPPER)) { + Map initialAttributes = initialSegment.getTracerAttributes(); + int initialThread; + if (initialAttributes.containsKey("thread.id")) { + initialThread = Integer.parseInt(initialAttributes.get("thread.id").toString()); + } else { + initialThread = -1; + } + Map attributes = child.getTracerAttributes(); + int childThread; + if (attributes.containsKey("thread.id")) { + childThread = Integer.parseInt(attributes.get("thread.id").toString()); + } else { + childThread = -1; + } + Assert.assertTrue(initialThread != childThread); + passes = true; + break; + } + } + Assert.assertTrue(passes); + } - sb.append("Name: ").append(segmentName); - sb.append(", Class: ").append(classname); - sb.append(", Method: ").append(methodName); - sb.append(", CallCount: ").append(callCount); - Map attributes = segment.getTracerAttributes(); - if(attributes != null) { - Set keys = attributes.keySet(); - for(String key : keys) { - Object value = attributes.get(key); - sb.append(", Attribute: ").append(key); - sb.append(": "); - sb.append(value); + } + + @Test + public void doMonoPublishOnTest() { + testMonoPub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn2); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + Collection traces = introspector.getTransactionTracesForTransaction(txn2); + System.out.println("Transaction traces: " + traces.size()); + for(TransactionTrace transactionTrace : traces) { + TraceSegment initialSegment = transactionTrace.getInitialTraceSegment(); + List children = initialSegment.getChildren(); + boolean passes = false; + /* + Assure that the publishing occurs on another thread + */ + for(TraceSegment child : children) { + if(child.getName().equals(WRAPPER)) { + Map initialAttributes = initialSegment.getTracerAttributes(); + int initialThread; + if(initialAttributes.containsKey("thread.id")) { + initialThread = Integer.parseInt(initialAttributes.get("thread.id").toString()); + } else { + initialThread = -1; + } + Map attributes = child.getTracerAttributes(); + int childThread; + if(attributes.containsKey("thread.id")) { + childThread = Integer.parseInt(attributes.get("thread.id").toString()); + } else { + childThread = -1; + } + Assert.assertTrue(initialThread != childThread); + passes = true; + } } + Assert.assertTrue(passes); + } - System.out.println(sb.toString()); - List children = segment.getChildren(); + + } + + @Test + public void doFluxPublishOnTest() { + testFluxPub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn4); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn4); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains("Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext")); + TracedMetricData metricData = metrics.get("Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext"); + Assert.assertNotNull(metricData); + Assert.assertEquals(4, metricData.getCallCount()); + + } + + @Test + public void doFluxSubscribeOnTest() { + testFluxSub(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn3); // & txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn3); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + } + + @Test + public void doScheduleTest() { + testScheduler(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + String txnName = introspector.getTransactionNames().iterator().next(); + Map metrics = introspector.getMetricsForTransaction(txnName); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Collection traces = introspector.getTransactionTracesForTransaction(txnName); + TransactionTrace transactionTrace = traces.iterator().next(); + Assert.assertNotNull(transactionTrace); + TraceSegment initial = transactionTrace.getInitialTraceSegment(); + Assert.assertNotNull(initial); + List children = initial.getChildren(); + boolean passes = false; + /* + Ensure that execution is dispatched to another thread and that Mono actions occur on that thread + */ for(TraceSegment child : children) { - printSegments(child, indents + 2); + if(child.getName().equals(WRAPPER)) { + List wrapperChildren = child.getChildren(); + Set childNames = new HashSet<>(); + for(TraceSegment wrapperChild : wrapperChildren) { + childNames.add(wrapperChild.getName()); + } + Assert.assertTrue(childNames.contains(MONO_SUBSCRIBE)); + Assert.assertTrue(childNames.contains(MONO_NEXT)); + Assert.assertTrue(childNames.contains(MONO_COMPLETE)); + } + } + } + + + @Trace(dispatcher = true) + public void testScheduler() { + System.out.println("Enter testScheduler"); + Mono mono = getStringMono(); + AwaitSingle await = new AwaitSingle(); + Scheduler scheduler = Schedulers.single(); + scheduler.schedule(() -> { + mono.subscribe(new TestMonoCoreSubscriber(await)); + }); + String result = await.await(); + System.out.println("TestScheduler result: " + result); + + } + + @Trace(dispatcher = true) + public void testMonoPub() { + System.out.println("Enter testMonoPub"); + AwaitSingle await = new AwaitSingle(); + Mono mono = getStringMono(); + + mono.publishOn(Schedulers.single()).subscribe(new TestMonoCoreSubscriber(await)); + await.await(); + + System.out.println("Exit testMonoPub with result: " + await.getResult()); + + } + + @Trace(dispatcher = true) + public void testMonoSub() { + System.out.println("Enter testMonoSub"); + AwaitSingle await = new AwaitSingle(); + + Mono mono = getStringMono().subscribeOn(Schedulers.single()).doOnSubscribe(new SubscriptionConsumer()); + + mono.subscribe(new TestMonoCoreSubscriber(await)); + String result = await.await(); + System.out.println("Exit testMonoSub with result: " + result); + + } + + @Trace(dispatcher = true) + public void testFluxSub() { + System.out.println("Enter testFluxSub"); + + Flux flux = getStringFlux().subscribeOn(Schedulers.single()); + + flux.subscribe(this::doSubscribeAction); + + List list = flux.collectList().block(); + System.out.println("Exit testFluxSub with result: " + list); + } + + @Trace(dispatcher = true) + public void testFluxPub() { + System.out.println("Enter testFluxPub"); + Flux flux = getStringFlux().publishOn(Schedulers.single()); + AwaitMany await = new AwaitMany(); + + flux.subscribe(new TestFluxCoreSubscriber(await,4)); + + List list = await.await(); + System.out.println("Exit testFluxPub with result: "+ list); + + } + + + public Flux getStringFlux() { + return Flux.fromArray(fluxArray); + } + + public Mono getStringMono() { + + + return Mono.fromCallable(() -> { + try { + Thread.sleep(100L); + } catch(Exception ignored) { + + } + return "hello"; + }); + } + + @Trace + public void doSubscribeAction(String s) { + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + e.printStackTrace(); } + System.out.println("Result is "+s); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java b/instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java index ffb4a47ac8..d852bd1868 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java @@ -21,7 +21,7 @@ public NRRunnableWrapper(Runnable r, Token t) { } @Override - @Trace(async=true) + @Trace(async = true, excludeFromTransactionTrace = true) public void run() { if(token != null) { token.linkAndExpire(); diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java index bfaf66d3bf..61dcabfdba 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -9,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.EmitterProcessor") public class EmitterProcessor_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -22,7 +21,7 @@ public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java index da12497ef9..054e9a7e30 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -23,7 +23,7 @@ static abstract class BaseSink_Instrumentation { token = NewRelic.getAgent().getTransaction().getToken(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void complete() { if (token != null) { token.linkAndExpire(); @@ -32,7 +32,7 @@ public void complete() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -52,7 +52,7 @@ public void cancel() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void request(long n) { if (token != null) { token.link(); @@ -69,7 +69,7 @@ static final class BufferAsyncSink_Instrumentation extends BaseSink_Instrumen super(actual); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); @@ -86,7 +86,7 @@ static final class IgnoreSink_Instrumentation extends BaseSink_Instrumentatio super(actual); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); @@ -102,7 +102,7 @@ static abstract class NoOverflowBaseAsyncSink_Instrumentation extends BaseSin super(actual); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); @@ -125,7 +125,7 @@ static final class FluxCreate$SerializedFluxSink_Instrumentation { } } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void complete() { if (token != null) { token.linkAndExpire(); @@ -134,7 +134,7 @@ public void complete() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable t) { if (token != null) { token.linkAndExpire(); @@ -143,7 +143,7 @@ public void error(Throwable t) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); @@ -166,7 +166,7 @@ static class SerializeOnRequestSink_Instrumentation { } } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void complete() { if (token != null) { token.linkAndExpire(); @@ -175,7 +175,7 @@ public void complete() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable t) { if (token != null) { token.linkAndExpire(); @@ -184,7 +184,7 @@ public void error(Throwable t) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java index 11df91ef56..12620031a7 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -22,7 +22,7 @@ static final class DefaultMonoSink_Instrumentation { this.token = NewRelic.getAgent().getTransaction().getToken(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void cancel() { if(token != null) { token.linkAndExpire(); @@ -31,7 +31,7 @@ public void cancel() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable e) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(e); @@ -43,7 +43,7 @@ public void error(Throwable e) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void success() { if(token != null) { token.linkAndExpire(); @@ -52,7 +52,7 @@ public void success() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void success(T value) { if(token != null) { token.linkAndExpire(); @@ -61,7 +61,7 @@ public void success(T value) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void request(long n) { if(token != null) { token.link(); diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java index 703b9c0e10..4bbfe04433 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java @@ -9,12 +9,12 @@ @Weave(originalName = "reactor.core.publisher.NextProcessor") class NextProcessor_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) Sinks.EmitResult tryEmitError(Throwable cause) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) Sinks.EmitResult tryEmitValue(O value) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java index 2191f9da4b..09aaae8fe4 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java @@ -9,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.ReplayProcessor") public class ReplayProcessor_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -22,7 +21,7 @@ public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java index ac01283c87..6a08756ab5 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java @@ -9,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.SinkEmptyMulticast") class SinkEmptyMulticast_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitEmpty() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java index 2606e489a0..346d6cc8da 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java @@ -10,12 +10,11 @@ @Weave(originalName = "reactor.core.publisher.SinkEmptySerialized", type = MatchType.BaseClass) class SinkEmptySerialized_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitEmpty() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java index 6511007406..3a4cc37ccd 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java @@ -9,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.SinkManyBestEffort") class SinkManyBestEffort_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -22,7 +21,7 @@ public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java index cdde2f5e33..81d384b6f4 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java @@ -9,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.SinkManySerialized") class SinkManySerialized_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -22,7 +21,7 @@ public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java index 3e4fb3e281..05096d4c55 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java @@ -7,7 +7,7 @@ @Weave(originalName = "reactor.core.publisher.SinkOneMulticast") class SinkOneMulticast_Instrumentation extends SinkEmptyMulticast_Instrumentation { - @Trace(async=true) + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitValue(O value) { return Weaver.callOriginal(); diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java index 92d8a5dc50..57a8af8c97 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java @@ -7,7 +7,7 @@ @Weave(originalName = "reactor.core.publisher.SinkOneSerialized") public abstract class SinkOneSerialized_Instrumentation extends SinkEmptySerialized_Instrumentation{ - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitValue(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java index 4c46de1eae..4f10a4d35f 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastManySinkNoBackpressure_Instrumentation.java @@ -9,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.UnicastManySinkNoBackpressure") class UnicastManySinkNoBackpressure_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -22,7 +21,7 @@ public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java index 780823a364..868972dd80 100644 --- a/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.4.10/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -9,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.UnicastProcessor") public class UnicastProcessor_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -22,7 +21,7 @@ public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java new file mode 100644 index 0000000000..3bd1c303c8 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java @@ -0,0 +1,37 @@ +package com.nr.instrumentation.reactor.test; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class AwaitMany { + + private List result = null; + private CompletableFuture> f; + + public AwaitMany() { + f = new CompletableFuture<>(); + } + + public List await() { + List s = Collections.emptyList(); + try { + s = f.get(); + } catch (InterruptedException | ExecutionException ignored) { + } + return s; + } + + public void onError(Throwable t) { + result.add(t.getMessage()); + f.completeExceptionally(t); + } + + public void done(List result) { + System.out.println("AwaitMany done"); + this.result = result; + f.complete(result); + } + +} diff --git a/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java new file mode 100644 index 0000000000..44434be273 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java @@ -0,0 +1,32 @@ +package com.nr.instrumentation.reactor.test; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class AwaitSingle { + + private String result = null; + private CompletableFuture f; + + public AwaitSingle() { + f = new CompletableFuture(); + } + + public String getResult() { + return result; + } + + public String await() { + String s = null; + try { + s = f.get(); + } catch (InterruptedException | ExecutionException ignored) { + } + return s; + } + + public void setResult(String s) { + result = s; + f.complete(result); + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java new file mode 100644 index 0000000000..acca8cd6f8 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java @@ -0,0 +1,33 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; + +public class MonoCoreSubscriber implements CoreSubscriber { + + @Override + @Trace + public void onNext(String t) { + System.out.println("Received string for onNext: " + t); + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Received error for onError: " + t); + } + + @Override + @Trace + public void onComplete() { + System.out.println("Mono has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription var1) { + System.out.println("Mono was subscribed to by : " + var1); + } + +} diff --git a/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java new file mode 100644 index 0000000000..b3a2dce717 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java @@ -0,0 +1,23 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; + +import java.util.function.Consumer; + +public class SubscriptionConsumer implements Consumer { + + @Override + @Trace + public void accept(Subscription subscription) { + pause(); + + } + + private void pause() { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + } +} diff --git a/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java new file mode 100644 index 0000000000..4f78dbd834 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java @@ -0,0 +1,414 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.agent.introspec.*; +import com.newrelic.api.agent.Trace; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.Sinks; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; + +import java.util.*; +import java.util.concurrent.*; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = "reactor.core") +public class TestApplication { + + + private static final String TRY_EMIT = "Java/reactor.core.publisher.SinkOneSerialized/tryEmitValue"; + private static final String TRY_EMIT2 = "Java/reactor.core.publisher.SinkOneMulticast/tryEmitValue"; + private static final String MONO_NEXT = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onNext"; + private static final String MONO_COMPLETE = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onComplete"; + private static final String MONO_SUBSCRIBE = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onSubscribe"; + private static final String BEST_EFFORT_NEXT = "Java/reactor.core.publisher.SinkManyBestEffort/tryEmitNext"; + private static final String SERIALIZED_NEXT = "Java/reactor.core.publisher.SinkManySerialized/tryEmitNext"; + private static final String SUBSCRIBER_NEXT = "Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext"; + private static final String SERIALIZED_COMPLETE = "Java/reactor.core.publisher.SinkManySerialized/tryEmitComplete"; + private static final String BEST_EFFORT_COMPLETE = "Java/reactor.core.publisher.SinkManyBestEffort/tryEmitComplete"; + private static final String txn1 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testMonoSub"; + private static final String txn2 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testMonoPub"; + private static final String txn3 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testFluxSub"; + private static final String txn4 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testFluxPub"; + private static final String WRAPPER = "Java/com.nr.instrumentation.reactor.NRRunnableWrapper/run"; + private static final String[] fluxArray = {"Message 1", "Message 2", "Message 3", "Message 4"}; + + @Test + public void doSinkOneTest() { + testSinkOne(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + String txnName = txnNames.iterator().next(); + + Map metrics = introspector.getMetricsForTransaction(txnName); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(TRY_EMIT)); + Assert.assertTrue(names.contains(TRY_EMIT2)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + } + + @Trace(dispatcher = true) + public void testSinkOne() { + + Sinks.One sink = Sinks.one(); + Mono mono = sink.asMono(); + + AwaitSingle awaitSingle = new AwaitSingle(); + mono.subscribe(new TestMonoCoreSubscriber(awaitSingle)); + + Schedulers.single().schedule(() -> { + Sinks.EmitResult result = sink.tryEmitValue("Hello"); + if(result.isFailure()) { + System.out.println("EmitResult threw an error: "+ result); + } + }); + + String result = awaitSingle.await(); + System.out.println("Await Single result : " + result); + } + + @Test + public void doTestSinkMany() { + testSinkMany(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + String txnName = txnNames.iterator().next(); + + Map metrics = introspector.getMetricsForTransaction(txnName); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(BEST_EFFORT_NEXT)); + Assert.assertTrue(names.contains(SERIALIZED_NEXT)); + Assert.assertTrue(names.contains(SUBSCRIBER_NEXT)); + Assert.assertTrue(names.contains(BEST_EFFORT_COMPLETE)); + Assert.assertTrue(names.contains(SERIALIZED_COMPLETE)); + + TracedMetricData metric = metrics.get(BEST_EFFORT_NEXT); + Assert.assertEquals(5, metric.getCallCount()); + + metric = metrics.get(SERIALIZED_NEXT); + Assert.assertEquals(5, metric.getCallCount()); + + metric = metrics.get(SUBSCRIBER_NEXT); + Assert.assertEquals(5, metric.getCallCount()); + + metric = metrics.get(BEST_EFFORT_COMPLETE); + Assert.assertEquals(1, metric.getCallCount()); + + metric = metrics.get(SERIALIZED_COMPLETE); + Assert.assertEquals(1, metric.getCallCount()); + + } + + @Trace(dispatcher = true) + public void testSinkMany() { + Sinks.Many hotSource = Sinks.many().multicast().directBestEffort(); + + Flux flux = hotSource.asFlux(); + AwaitMany awaitMany = new AwaitMany(); + flux.subscribe(new TestFluxCoreSubscriber(awaitMany, 5)); + + Scheduler scheduler = Schedulers.single(); + String[] colors = new String[]{"Red", "Green", "Blue", "Pink", "Purple"}; + for(String color : colors) { + CompletableFuture completableFuture = new CompletableFuture<>(); + scheduler.schedule(() -> { + hotSource.emitNext(color, Sinks.EmitFailureHandler.FAIL_FAST); + completableFuture.complete(true); + }, 100, TimeUnit.MILLISECONDS); + + try { + Boolean result = completableFuture.get(); + } catch (InterruptedException | ExecutionException ignored) { + } + + } + hotSource.emitComplete(Sinks.EmitFailureHandler.FAIL_FAST); + + List results = awaitMany.await(); + System.out.println("Await Many result : " + results); + } + + @Test + public void doMonoSubscribeOnTest() { + testMonoSub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transactions", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn1); // & txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn1); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + Collection traces = introspector.getTransactionTracesForTransaction(txn1); + for (TransactionTrace transactionTrace : traces) { + TraceSegment initialSegment = transactionTrace.getInitialTraceSegment(); + List children = initialSegment.getChildren(); + boolean passes = false; + /* + Assure that the subscribing occurs on another thread + */ + for (TraceSegment child : children) { + if (child.getName().equals(WRAPPER)) { + Map initialAttributes = initialSegment.getTracerAttributes(); + int initialThread; + if (initialAttributes.containsKey("thread.id")) { + initialThread = Integer.parseInt(initialAttributes.get("thread.id").toString()); + } else { + initialThread = -1; + } + Map attributes = child.getTracerAttributes(); + int childThread; + if (attributes.containsKey("thread.id")) { + childThread = Integer.parseInt(attributes.get("thread.id").toString()); + } else { + childThread = -1; + } + Assert.assertTrue(initialThread != childThread); + passes = true; + break; + } + } + Assert.assertTrue(passes); + + } + } + + @Test + public void doMonoPublishOnTest() { + testMonoPub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn2); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + Collection traces = introspector.getTransactionTracesForTransaction(txn2); + for(TransactionTrace transactionTrace : traces) { + TraceSegment initialSegment = transactionTrace.getInitialTraceSegment(); + List children = initialSegment.getChildren(); + boolean passes = false; + /* + Assure that the publishing occurs on another thread + */ + for(TraceSegment child : children) { + if(child.getName().equals(WRAPPER)) { + Map initialAttributes = initialSegment.getTracerAttributes(); + int initialThread; + if(initialAttributes.containsKey("thread.id")) { + initialThread = Integer.parseInt(initialAttributes.get("thread.id").toString()); + } else { + initialThread = -1; + } + Map attributes = child.getTracerAttributes(); + int childThread; + if(attributes.containsKey("thread.id")) { + childThread = Integer.parseInt(attributes.get("thread.id").toString()); + } else { + childThread = -1; + } + Assert.assertTrue(initialThread != childThread); + passes = true; + } + } + Assert.assertTrue(passes); + + } + + } + + @Test + public void doFluxPublishOnTest() { + testFluxPub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn4); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn4); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains("Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext")); + TracedMetricData metricData = metrics.get("Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext"); + Assert.assertNotNull(metricData); + Assert.assertEquals(4, metricData.getCallCount()); + + } + + @Test + public void doFluxSubscribeOnTest() { + testFluxSub(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn3); // & txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn3); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + } + + @Test + public void doScheduleTest() { + testScheduler(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + String txnName = introspector.getTransactionNames().iterator().next(); + Map metrics = introspector.getMetricsForTransaction(txnName); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Collection traces = introspector.getTransactionTracesForTransaction(txnName); + TransactionTrace transactionTrace = traces.iterator().next(); + Assert.assertNotNull(transactionTrace); + TraceSegment initial = transactionTrace.getInitialTraceSegment(); + Assert.assertNotNull(initial); + List children = initial.getChildren(); + boolean passes = false; + /* + Ensure that execution is dispatched to another thread and that Mono actions occur on that thread + */ + for(TraceSegment child : children) { + if(child.getName().equals(WRAPPER)) { + List wrapperChildren = child.getChildren(); + Set childNames = new HashSet<>(); + for(TraceSegment wrapperChild : wrapperChildren) { + childNames.add(wrapperChild.getName()); + } + Assert.assertTrue(childNames.contains(MONO_SUBSCRIBE)); + Assert.assertTrue(childNames.contains(MONO_NEXT)); + Assert.assertTrue(childNames.contains(MONO_COMPLETE)); + } + } + } + + + @Trace(dispatcher = true) + public void testScheduler() { + System.out.println("Enter testScheduler"); + Mono mono = getStringMono(); + AwaitSingle await = new AwaitSingle(); + Scheduler scheduler = Schedulers.single(); + scheduler.schedule(() -> { + mono.subscribe(new TestMonoCoreSubscriber(await)); + }); + String result = await.await(); + System.out.println("TestScheduler result: " + result); + + } + + @Trace(dispatcher = true) + public void testMonoPub() { + System.out.println("Enter testMonoPub"); + AwaitSingle await = new AwaitSingle(); + Mono mono = getStringMono(); + + mono.publishOn(Schedulers.single()).subscribe(new TestMonoCoreSubscriber(await)); + await.await(); + + System.out.println("Exit testMonoPub with result: " + await.getResult()); + + } + + @Trace(dispatcher = true) + public void testMonoSub() { + System.out.println("Enter testMonoSub"); + AwaitSingle await = new AwaitSingle(); + + Mono mono = getStringMono().subscribeOn(Schedulers.single()).doOnSubscribe(new SubscriptionConsumer()); + + mono.subscribe(new TestMonoCoreSubscriber(await)); + String result = await.await(); + System.out.println("Exit testMonoSub with result: " + result); + + } + + @Trace(dispatcher = true) + public void testFluxSub() { + System.out.println("Enter testFluxSub"); + + Flux flux = getStringFlux().subscribeOn(Schedulers.single()); + + flux.subscribe(this::doSubscribeAction); + + List list = flux.collectList().block(); + System.out.println("Exit testFluxSub with result: " + list); + } + + @Trace(dispatcher = true) + public void testFluxPub() { + System.out.println("Enter testFluxPub"); + Flux flux = getStringFlux().publishOn(Schedulers.single()); + AwaitMany await = new AwaitMany(); + + flux.subscribe(new TestFluxCoreSubscriber(await,4)); + + List list = await.await(); + System.out.println("Exit testFluxPub with result: "+ list); + + } + + + public Flux getStringFlux() { + return Flux.fromArray(fluxArray); + } + + public Mono getStringMono() { + + + return Mono.fromCallable(() -> { + try { + Thread.sleep(100L); + } catch(Exception ignored) { + + } + return "hello"; + }); + } + + @Trace + public void doSubscribeAction(String s) { + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Result is "+s); + } + + +} diff --git a/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java new file mode 100644 index 0000000000..0d87eb60d0 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java @@ -0,0 +1,52 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; + +import java.util.ArrayList; +import java.util.List; + +public class TestFluxCoreSubscriber implements CoreSubscriber { + + private AwaitMany await = null; + List result = null; + private int numberOfItems = 0; + public TestFluxCoreSubscriber(AwaitMany a, int numberOfItems) { + result = new ArrayList<>(); + await = a; + this.numberOfItems = numberOfItems; + } + + @Trace + public void onNext(String t) { + System.out.println("call to onNext with string: " + t); + result.add(t); + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Object has error: " + t.getMessage()); + if (await != null) { + List result = new ArrayList<>(); + result.add(t.getMessage()); + await.done(result); + } + + } + + @Override + @Trace + public void onComplete() { + await.done(result); + System.out.println("Object has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription s) { + s.request(numberOfItems); + } + +} diff --git a/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java new file mode 100644 index 0000000000..0648030287 --- /dev/null +++ b/instrumentation/reactor-core-3.4.10/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java @@ -0,0 +1,50 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; + +public class TestMonoCoreSubscriber implements CoreSubscriber { + + private AwaitSingle await = null; + String result = null; + + public TestMonoCoreSubscriber(AwaitSingle a) { + await = a; + } + + @Override + @Trace + public void onNext(String t) { + System.out.println("call to onNext with string: " + t); + result = t; + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Object has error: "+t.getMessage()); + if(await != null) { + await.setResult(t.getMessage()); + } + + } + + @Override + @Trace + public void onComplete() { + if(await != null) { + synchronized(await) { + await.setResult(result); + } + } + System.out.println("Object has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription s) { + s.request(1); + } + +} diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java b/instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java index ffb4a47ac8..d852bd1868 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/com/nr/instrumentation/reactor/NRRunnableWrapper.java @@ -21,7 +21,7 @@ public NRRunnableWrapper(Runnable r, Token t) { } @Override - @Trace(async=true) + @Trace(async = true, excludeFromTransactionTrace = true) public void run() { if(token != null) { token.linkAndExpire(); diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java index 79675a97e3..127a9b224f 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/EmitterProcessor_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,17 +9,17 @@ @Weave(originalName = "reactor.core.publisher.EmitterProcessor") public class EmitterProcessor_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java index a3e3ebff32..e47e9d4302 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/FluxCreate_Instrumentation.java @@ -23,7 +23,7 @@ static abstract class BaseSink_Instrumentation { token = NewRelic.getAgent().getTransaction().getToken(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void complete() { if (token != null) { token.linkAndExpire(); @@ -32,7 +32,7 @@ public void complete() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable e) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(e); @@ -52,7 +52,7 @@ public void cancel() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void request(long n) { if (token != null) { token.link(); @@ -69,7 +69,7 @@ static final class BufferAsyncSink_Instrumentation extends BaseSink_Instrumen super(actual); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); @@ -86,7 +86,7 @@ static final class IgnoreSink_Instrumentation extends BaseSink_Instrumentatio super(actual); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); @@ -102,7 +102,7 @@ static abstract class NoOverflowBaseAsyncSink_Instrumentation extends BaseSin super(actual); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); @@ -125,7 +125,7 @@ static final class FluxCreate$SerializedFluxSink_Instrumentation { } } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void complete() { if (token != null) { token.linkAndExpire(); @@ -134,7 +134,7 @@ public void complete() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable t) { if (token != null) { token.linkAndExpire(); @@ -143,7 +143,7 @@ public void error(Throwable t) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); @@ -166,7 +166,7 @@ static class SerializeOnRequestSink_Instrumentation { } } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void complete() { if (token != null) { token.linkAndExpire(); @@ -175,7 +175,7 @@ public void complete() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable t) { if (token != null) { token.linkAndExpire(); @@ -184,7 +184,7 @@ public void error(Throwable t) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public FluxSink next(T t) { if (token != null) { token.link(); diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java index 954dfa3386..12620031a7 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/MonoCreate_Instrumentation.java @@ -8,7 +8,6 @@ import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; import reactor.core.CoreSubscriber; -import reactor.util.annotation.Nullable; @Weave(originalName = "reactor.core.publisher.MonoCreate") class MonoCreate_Instrumentation { @@ -23,7 +22,7 @@ static final class DefaultMonoSink_Instrumentation { this.token = NewRelic.getAgent().getTransaction().getToken(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void cancel() { if(token != null) { token.linkAndExpire(); @@ -32,7 +31,7 @@ public void cancel() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void error(Throwable e) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(e); @@ -44,7 +43,7 @@ public void error(Throwable e) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void success() { if(token != null) { token.linkAndExpire(); @@ -53,7 +52,7 @@ public void success() { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void success(T value) { if(token != null) { token.linkAndExpire(); @@ -62,7 +61,7 @@ public void success(T value) { Weaver.callOriginal(); } - @Trace(async = true) + @Trace(async = true, excludeFromTransactionTrace = true) public void request(long n) { if(token != null) { token.link(); diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java index 513b9e22a1..4a75132292 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/NextProcessor_Instrumentation.java @@ -1,23 +1,20 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; -import reactor.core.CorePublisher; @Weave(originalName = "reactor.core.publisher.NextProcessor") class NextProcessor_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) Sinks.EmitResult tryEmitError(Throwable cause) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) Sinks.EmitResult tryEmitValue(O value) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java index 207e77ce97..09aaae8fe4 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/ReplayProcessor_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.ReplayProcessor") public class ReplayProcessor_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -24,7 +21,7 @@ public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java index 8638ee5741..6a08756ab5 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptyMulticast_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.SinkEmptyMulticast") class SinkEmptyMulticast_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitEmpty() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java index b8d40df41b..346d6cc8da 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkEmptySerialized_Instrumentation.java @@ -1,10 +1,8 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.MatchType; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -12,12 +10,11 @@ @Weave(originalName = "reactor.core.publisher.SinkEmptySerialized", type = MatchType.BaseClass) class SinkEmptySerialized_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitEmpty() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java index 3870e2beba..3a4cc37ccd 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManyBestEffort_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.SinkManyBestEffort") class SinkManyBestEffort_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -24,7 +21,7 @@ public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java index bad42bfe41..81d384b6f4 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkManySerialized_Instrumentation.java @@ -1,9 +1,7 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; @@ -11,12 +9,11 @@ @Weave(originalName = "reactor.core.publisher.SinkManySerialized") class SinkManySerialized_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace public Sinks.EmitResult tryEmitError(Throwable t) { if(ReactorConfig.errorsEnabled) { NewRelic.noticeError(t); @@ -24,7 +21,7 @@ public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java index 3e4fb3e281..05096d4c55 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneMulticast_Instrumentation.java @@ -7,7 +7,7 @@ @Weave(originalName = "reactor.core.publisher.SinkOneMulticast") class SinkOneMulticast_Instrumentation extends SinkEmptyMulticast_Instrumentation { - @Trace(async=true) + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitValue(O value) { return Weaver.callOriginal(); diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java index 92d8a5dc50..57a8af8c97 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/SinkOneSerialized_Instrumentation.java @@ -7,7 +7,7 @@ @Weave(originalName = "reactor.core.publisher.SinkOneSerialized") public abstract class SinkOneSerialized_Instrumentation extends SinkEmptySerialized_Instrumentation{ - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitValue(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java index d5ac8f3c04..5ff3003b34 100644 --- a/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java +++ b/instrumentation/reactor-core-3.5.0/src/main/java/reactor/core/publisher/UnicastProcessor_Instrumentation.java @@ -1,31 +1,25 @@ package reactor.core.publisher; import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Token; import com.newrelic.api.agent.Trace; -import com.newrelic.api.agent.weaver.NewField; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.instrumentation.reactor.ReactorConfig; -import reactor.core.Disposable; - -import java.util.Queue; -import java.util.function.Consumer; @Weave(originalName = "reactor.core.publisher.UnicastProcessor") public class UnicastProcessor_Instrumentation { - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitComplete() { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitError(Throwable t) { return Weaver.callOriginal(); } - @Trace + @Trace(excludeFromTransactionTrace = true) public Sinks.EmitResult tryEmitNext(T t) { return Weaver.callOriginal(); } diff --git a/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java new file mode 100644 index 0000000000..3bd1c303c8 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitMany.java @@ -0,0 +1,37 @@ +package com.nr.instrumentation.reactor.test; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class AwaitMany { + + private List result = null; + private CompletableFuture> f; + + public AwaitMany() { + f = new CompletableFuture<>(); + } + + public List await() { + List s = Collections.emptyList(); + try { + s = f.get(); + } catch (InterruptedException | ExecutionException ignored) { + } + return s; + } + + public void onError(Throwable t) { + result.add(t.getMessage()); + f.completeExceptionally(t); + } + + public void done(List result) { + System.out.println("AwaitMany done"); + this.result = result; + f.complete(result); + } + +} diff --git a/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java new file mode 100644 index 0000000000..44434be273 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/AwaitSingle.java @@ -0,0 +1,32 @@ +package com.nr.instrumentation.reactor.test; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class AwaitSingle { + + private String result = null; + private CompletableFuture f; + + public AwaitSingle() { + f = new CompletableFuture(); + } + + public String getResult() { + return result; + } + + public String await() { + String s = null; + try { + s = f.get(); + } catch (InterruptedException | ExecutionException ignored) { + } + return s; + } + + public void setResult(String s) { + result = s; + f.complete(result); + } +} diff --git a/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java new file mode 100644 index 0000000000..acca8cd6f8 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/MonoCoreSubscriber.java @@ -0,0 +1,33 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; + +public class MonoCoreSubscriber implements CoreSubscriber { + + @Override + @Trace + public void onNext(String t) { + System.out.println("Received string for onNext: " + t); + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Received error for onError: " + t); + } + + @Override + @Trace + public void onComplete() { + System.out.println("Mono has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription var1) { + System.out.println("Mono was subscribed to by : " + var1); + } + +} diff --git a/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java new file mode 100644 index 0000000000..b3a2dce717 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/SubscriptionConsumer.java @@ -0,0 +1,23 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; + +import java.util.function.Consumer; + +public class SubscriptionConsumer implements Consumer { + + @Override + @Trace + public void accept(Subscription subscription) { + pause(); + + } + + private void pause() { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + } +} diff --git a/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java new file mode 100644 index 0000000000..1476a1fa84 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/TestApplication.java @@ -0,0 +1,426 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.agent.introspec.*; +import com.newrelic.api.agent.Trace; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.publisher.Sinks; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = "reactor.core") +public class TestApplication { + + + private static final String TRY_EMIT = "Java/reactor.core.publisher.SinkOneSerialized/tryEmitValue"; + private static final String TRY_EMIT2 = "Java/reactor.core.publisher.SinkOneMulticast/tryEmitValue"; + private static final String MONO_NEXT = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onNext"; + private static final String MONO_COMPLETE = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onComplete"; + private static final String MONO_SUBSCRIBE = "Custom/com.nr.instrumentation.reactor.test.TestMonoCoreSubscriber/onSubscribe"; + private static final String BEST_EFFORT_NEXT = "Java/reactor.core.publisher.SinkManyBestEffort/tryEmitNext"; + private static final String SERIALIZED_NEXT = "Java/reactor.core.publisher.SinkManySerialized/tryEmitNext"; + private static final String SUBSCRIBER_NEXT = "Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext"; + private static final String SERIALIZED_COMPLETE = "Java/reactor.core.publisher.SinkManySerialized/tryEmitComplete"; + private static final String BEST_EFFORT_COMPLETE = "Java/reactor.core.publisher.SinkManyBestEffort/tryEmitComplete"; + private static final String txn1 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testMonoSub"; + private static final String txn2 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testMonoPub"; + private static final String txn3 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testFluxSub"; + private static final String txn4 = "OtherTransaction/Custom/com.nr.instrumentation.reactor.test.TestApplication/testFluxPub"; + private static final String WRAPPER = "Java/com.nr.instrumentation.reactor.NRRunnableWrapper/run"; + private static final String[] fluxArray = {"Message 1", "Message 2", "Message 3", "Message 4"}; + + @Test + public void doSinkOneTest() { + testSinkOne(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + String txnName = txnNames.iterator().next(); + + Map metrics = introspector.getMetricsForTransaction(txnName); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(TRY_EMIT)); + Assert.assertTrue(names.contains(TRY_EMIT2)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + } + + @Trace(dispatcher = true) + public void testSinkOne() { + + Sinks.One sink = Sinks.one(); + Mono mono = sink.asMono(); + + AwaitSingle awaitSingle = new AwaitSingle(); + mono.subscribe(new TestMonoCoreSubscriber(awaitSingle)); + + Schedulers.single().schedule(() -> { + Sinks.EmitResult result = sink.tryEmitValue("Hello"); + if(result.isFailure()) { + System.out.println("EmitResult threw an error: "+ result); + } + }); + + String result = awaitSingle.await(); + System.out.println("Await Single result : " + result); + } + + @Test + public void doTestSinkMany() { + testSinkMany(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + String txnName = txnNames.iterator().next(); + + Map metrics = introspector.getMetricsForTransaction(txnName); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(BEST_EFFORT_NEXT)); + Assert.assertTrue(names.contains(SERIALIZED_NEXT)); + Assert.assertTrue(names.contains(SUBSCRIBER_NEXT)); + Assert.assertTrue(names.contains(BEST_EFFORT_COMPLETE)); + Assert.assertTrue(names.contains(SERIALIZED_COMPLETE)); + + TracedMetricData metric = metrics.get(BEST_EFFORT_NEXT); + Assert.assertEquals(5, metric.getCallCount()); + + metric = metrics.get(SERIALIZED_NEXT); + Assert.assertEquals(5, metric.getCallCount()); + + metric = metrics.get(SUBSCRIBER_NEXT); + Assert.assertEquals(5, metric.getCallCount()); + + metric = metrics.get(BEST_EFFORT_COMPLETE); + Assert.assertEquals(1, metric.getCallCount()); + + metric = metrics.get(SERIALIZED_COMPLETE); + Assert.assertEquals(1, metric.getCallCount()); + + } + + @Trace(dispatcher = true) + public void testSinkMany() { + Sinks.Many hotSource = Sinks.many().multicast().directBestEffort(); + + Flux flux = hotSource.asFlux(); + AwaitMany awaitMany = new AwaitMany(); + flux.subscribe(new TestFluxCoreSubscriber(awaitMany, 5)); + + Scheduler scheduler = Schedulers.single(); + String[] colors = new String[]{"Red", "Green", "Blue", "Pink", "Purple"}; + for(String color : colors) { + CompletableFuture completableFuture = new CompletableFuture<>(); + scheduler.schedule(() -> { + hotSource.emitNext(color, Sinks.EmitFailureHandler.FAIL_FAST); + completableFuture.complete(true); + }, 100, TimeUnit.MILLISECONDS); + + try { + Boolean result = completableFuture.get(); + } catch (InterruptedException | ExecutionException ignored) { + } + + } + hotSource.emitComplete(Sinks.EmitFailureHandler.FAIL_FAST); + + List results = awaitMany.await(); + System.out.println("Await Many result : " + results); + } + + @Test + public void doMonoSubscribeOnTest() { + testMonoSub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transactions", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn1); // & txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn1); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + Collection traces = introspector.getTransactionTracesForTransaction(txn1); + for (TransactionTrace transactionTrace : traces) { + TraceSegment initialSegment = transactionTrace.getInitialTraceSegment(); + List children = initialSegment.getChildren(); + boolean passes = false; + /* + Assure that the subscribing occurs on another thread + */ + for (TraceSegment child : children) { + if (child.getName().equals(WRAPPER)) { + Map initialAttributes = initialSegment.getTracerAttributes(); + int initialThread; + if (initialAttributes.containsKey("thread.id")) { + initialThread = Integer.parseInt(initialAttributes.get("thread.id").toString()); + } else { + initialThread = -1; + } + Map attributes = child.getTracerAttributes(); + int childThread; + if (attributes.containsKey("thread.id")) { + childThread = Integer.parseInt(attributes.get("thread.id").toString()); + } else { + childThread = -1; + } + Assert.assertTrue(initialThread != childThread); + passes = true; + break; + } + } + Assert.assertTrue(passes); + + } + } + + @Test + public void doMonoPublishOnTest() { + testMonoPub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn2); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains(MONO_NEXT)); + Assert.assertTrue(names.contains(MONO_COMPLETE)); + + Collection traces = introspector.getTransactionTracesForTransaction(txn2); + for(TransactionTrace transactionTrace : traces) { + TraceSegment initialSegment = transactionTrace.getInitialTraceSegment(); + List children = initialSegment.getChildren(); + boolean passes = false; + /* + Assure that the publishing occurs on another thread + */ + for(TraceSegment child : children) { + if(child.getName().equals(WRAPPER)) { + Map initialAttributes = initialSegment.getTracerAttributes(); + int initialThread; + if(initialAttributes.containsKey("thread.id")) { + initialThread = Integer.parseInt(initialAttributes.get("thread.id").toString()); + } else { + initialThread = -1; + } + Map attributes = child.getTracerAttributes(); + int childThread; + if(attributes.containsKey("thread.id")) { + childThread = Integer.parseInt(attributes.get("thread.id").toString()); + } else { + childThread = -1; + } + Assert.assertTrue(initialThread != childThread); + passes = true; + } + } + Assert.assertTrue(passes); + + } + + } + + @Test + public void doFluxPublishOnTest() { + testFluxPub(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn4); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn4); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Assert.assertTrue(names.contains("Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext")); + TracedMetricData metricData = metrics.get("Custom/com.nr.instrumentation.reactor.test.TestFluxCoreSubscriber/onNext"); + Assert.assertNotNull(metricData); + Assert.assertEquals(4, metricData.getCallCount()); + + } + + @Test + public void doFluxSubscribeOnTest() { + testFluxSub(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + + Collection txnNames = introspector.getTransactionNames(); + boolean contains = txnNames.contains(txn3); // & txnNames.contains(txn2); + Assert.assertTrue(contains); + + Map metrics = introspector.getMetricsForTransaction(txn3); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + } + + @Test + public void doScheduleTest() { + testScheduler(); + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + int finishedTransactionCount = introspector.getFinishedTransactionCount(5000); + Assert.assertEquals("Expected one transaction", 1, finishedTransactionCount); + String txnName = introspector.getTransactionNames().iterator().next(); + Map metrics = introspector.getMetricsForTransaction(txnName); + Set names = metrics.keySet(); + Assert.assertTrue(names.contains(WRAPPER)); + Collection traces = introspector.getTransactionTracesForTransaction(txnName); + TransactionTrace transactionTrace = traces.iterator().next(); + Assert.assertNotNull(transactionTrace); + TraceSegment initial = transactionTrace.getInitialTraceSegment(); + Assert.assertNotNull(initial); + List children = initial.getChildren(); + boolean passes = false; + /* + Ensure that execution is dispatched to another thread and that Mono actions occur on that thread + */ + for(TraceSegment child : children) { + if(child.getName().equals(WRAPPER)) { + List wrapperChildren = child.getChildren(); + Set childNames = new HashSet<>(); + TraceSegment subscribeTrace = null; + for(TraceSegment wrapperChild : wrapperChildren) { + String childName = wrapperChild.getName(); + childNames.add(wrapperChild.getName()); + if(childName.equals(MONO_SUBSCRIBE)) subscribeTrace = wrapperChild; + } + Assert.assertTrue(childNames.contains(MONO_SUBSCRIBE)); + Assert.assertNotNull(subscribeTrace); + List subscriberChildren = subscribeTrace.getChildren(); + childNames.clear(); + for(TraceSegment subscriberChild : subscriberChildren) { + String childName = subscriberChild.getName(); + childNames.add(subscriberChild.getName()); + } + Assert.assertTrue(childNames.contains(MONO_NEXT)); + Assert.assertTrue(childNames.contains(MONO_COMPLETE)); + } + } + } + + + @Trace(dispatcher = true) + public void testScheduler() { + System.out.println("Enter testScheduler"); + Mono mono = getStringMono(); + AwaitSingle await = new AwaitSingle(); + Scheduler scheduler = Schedulers.single(); + scheduler.schedule(() -> { + mono.subscribe(new TestMonoCoreSubscriber(await)); + }); + String result = await.await(); + System.out.println("TestScheduler result: " + result); + + } + + @Trace(dispatcher = true) + public void testMonoPub() { + System.out.println("Enter testMonoPub"); + AwaitSingle await = new AwaitSingle(); + Mono mono = getStringMono(); + + mono.publishOn(Schedulers.single()).subscribe(new TestMonoCoreSubscriber(await)); + await.await(); + + System.out.println("Exit testMonoPub with result: " + await.getResult()); + + } + + @Trace(dispatcher = true) + public void testMonoSub() { + System.out.println("Enter testMonoSub"); + AwaitSingle await = new AwaitSingle(); + + Mono mono = getStringMono().subscribeOn(Schedulers.single()).doOnSubscribe(new SubscriptionConsumer()); + + mono.subscribe(new TestMonoCoreSubscriber(await)); + String result = await.await(); + System.out.println("Exit testMonoSub with result: " + result); + + } + + @Trace(dispatcher = true) + public void testFluxSub() { + System.out.println("Enter testFluxSub"); + + Flux flux = getStringFlux().subscribeOn(Schedulers.single()); + + flux.subscribe(this::doSubscribeAction); + + List list = flux.collectList().block(); + System.out.println("Exit testFluxSub with result: " + list); + } + + @Trace(dispatcher = true) + public void testFluxPub() { + System.out.println("Enter testFluxPub"); + Flux flux = getStringFlux().publishOn(Schedulers.single()); + AwaitMany await = new AwaitMany(); + + flux.subscribe(new TestFluxCoreSubscriber(await,4)); + + List list = await.await(); + System.out.println("Exit testFluxPub with result: "+ list); + + } + + + public Flux getStringFlux() { + return Flux.fromArray(fluxArray); + } + + public Mono getStringMono() { + + + return Mono.fromCallable(() -> { + try { + Thread.sleep(100L); + } catch(Exception ignored) { + + } + return "hello"; + }); + } + + @Trace + public void doSubscribeAction(String s) { + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Result is "+s); + } + + +} diff --git a/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java new file mode 100644 index 0000000000..0d87eb60d0 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/TestFluxCoreSubscriber.java @@ -0,0 +1,52 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; + +import java.util.ArrayList; +import java.util.List; + +public class TestFluxCoreSubscriber implements CoreSubscriber { + + private AwaitMany await = null; + List result = null; + private int numberOfItems = 0; + public TestFluxCoreSubscriber(AwaitMany a, int numberOfItems) { + result = new ArrayList<>(); + await = a; + this.numberOfItems = numberOfItems; + } + + @Trace + public void onNext(String t) { + System.out.println("call to onNext with string: " + t); + result.add(t); + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Object has error: " + t.getMessage()); + if (await != null) { + List result = new ArrayList<>(); + result.add(t.getMessage()); + await.done(result); + } + + } + + @Override + @Trace + public void onComplete() { + await.done(result); + System.out.println("Object has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription s) { + s.request(numberOfItems); + } + +} diff --git a/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java new file mode 100644 index 0000000000..0648030287 --- /dev/null +++ b/instrumentation/reactor-core-3.5.0/src/test/java/com/nr/instrumentation/reactor/test/TestMonoCoreSubscriber.java @@ -0,0 +1,50 @@ +package com.nr.instrumentation.reactor.test; + +import com.newrelic.api.agent.Trace; +import org.reactivestreams.Subscription; +import reactor.core.CoreSubscriber; + +public class TestMonoCoreSubscriber implements CoreSubscriber { + + private AwaitSingle await = null; + String result = null; + + public TestMonoCoreSubscriber(AwaitSingle a) { + await = a; + } + + @Override + @Trace + public void onNext(String t) { + System.out.println("call to onNext with string: " + t); + result = t; + } + + @Override + @Trace + public void onError(Throwable t) { + System.out.println("Object has error: "+t.getMessage()); + if(await != null) { + await.setResult(t.getMessage()); + } + + } + + @Override + @Trace + public void onComplete() { + if(await != null) { + synchronized(await) { + await.setResult(result); + } + } + System.out.println("Object has completed"); + } + + @Override + @Trace + public void onSubscribe(Subscription s) { + s.request(1); + } + +}