From 8da618a0418d97f45d1144a3f32e8170fa8655c6 Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Tue, 31 Oct 2023 07:34:38 -0400 Subject: [PATCH 01/16] WIP commit --- instrumentation/vertx-core-4.0.0/.gitignore | 42 ++ instrumentation/vertx-core-4.0.0/build.gradle | 25 ++ .../instrumentation/AsyncHandlerWrapper.java | 37 ++ .../vertx/instrumentation/InboundWrapper.java | 32 ++ .../instrumentation/OutboundWrapper.java | 32 ++ .../PromiseHandlerWrapper.java | 37 ++ .../vertx/instrumentation/VertxCoreUtil.java | 72 ++++ ...HttpClientRequestBase_Instrumentation.java | 107 +++++ ...HttpClientRequestImpl_Instrumentation.java | 74 ++++ .../impl/AbstractContext_Instrumentation.java | 24 ++ .../impl/ContextImpl_Instrumentation.java | 25 ++ .../future/FutureImpl_Instrumentation.java | 84 ++++ .../instrumentation/VertxBlockingTest.java | 59 +++ .../nr/vertx/instrumentation/VertxClient.java | 379 ++++++++++++++++++ .../nr/vertx/instrumentation/VertxFuture.java | 200 +++++++++ instrumentation/vertx-core-4.3.2/.gitignore | 42 ++ instrumentation/vertx-core-4.3.2/build.gradle | 25 ++ .../vertx/instrumentation/InboundWrapper.java | 32 ++ .../instrumentation/OutboundWrapper.java | 32 ++ .../vertx/instrumentation/VertxCoreUtil.java | 71 ++++ ...HttpClientRequestBase_Instrumentation.java | 64 +++ ...HttpClientRequestImpl_Instrumentation.java | 64 +++ .../impl/ContextBase_Instrumentation.java | 36 ++ .../core/impl/FutureImpl_Instrumentation.java | 48 +++ .../instrumentation/VertxBlockingTest.java | 60 +++ .../nr/vertx/instrumentation/VertxClient.java | 348 ++++++++++++++++ .../nr/vertx/instrumentation/VertxFuture.java | 105 +++++ .../main/java/com/newrelic/agent/Segment.java | 2 + .../java/com/newrelic/agent/TokenImpl.java | 1 + .../java/com/newrelic/agent/Transaction.java | 1 + .../newrelic/agent/TransactionApiImpl.java | 3 + settings.gradle | 2 + 32 files changed, 2165 insertions(+) create mode 100644 instrumentation/vertx-core-4.0.0/.gitignore create mode 100644 instrumentation/vertx-core-4.0.0/build.gradle create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java create mode 100644 instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java create mode 100644 instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java create mode 100644 instrumentation/vertx-core-4.3.2/.gitignore create mode 100644 instrumentation/vertx-core-4.3.2/build.gradle create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java create mode 100644 instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java create mode 100644 instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java diff --git a/instrumentation/vertx-core-4.0.0/.gitignore b/instrumentation/vertx-core-4.0.0/.gitignore new file mode 100644 index 0000000000..b63da4551b --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/instrumentation/vertx-core-4.0.0/build.gradle b/instrumentation/vertx-core-4.0.0/build.gradle new file mode 100644 index 0000000000..e17b5f6b24 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/build.gradle @@ -0,0 +1,25 @@ +jar { + manifest { + attributes 'Implementation-Title': 'com.newrelic.instrumentation.vertx-core-4.0.0' + } +} + + + +dependencies { + implementation(project(":agent-bridge")) + implementation("io.vertx:vertx-core:4.0.0") + testImplementation("io.vertx:vertx-core:4.0.0") +} + +verifyInstrumentation { + passesOnly 'io.vertx:vertx-core:[4.0.0,4.3.2)' + excludeRegex '.*CR[0-9]*' + excludeRegex '.*-milestone[0-9]' + excludeRegex '.*Beta[0-9]' +} + +site { + title 'Vertx' + type 'Framework' +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java new file mode 100644 index 0000000000..3a31ccd31c --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java @@ -0,0 +1,37 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; + +public class AsyncHandlerWrapper implements Handler> { + private Handler> original; + + private Token token; + + public AsyncHandlerWrapper(Handler> original, Token token) { + System.out.println("------- Construct AsyncHandlerWrapper "+ token); + this.original = original; + this.token = token; + } + + @Override + @Trace(async = true, excludeFromTransactionTrace = true) + public void handle(AsyncResult event) { + if (token != null) { + System.out.println("------- Link&Expire " + this.token); + token.linkAndExpire(); + token = null; + } + + this.original.handle(event); + this.original = null; + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java new file mode 100644 index 0000000000..41531affba --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.InboundHeaders; +import io.vertx.core.http.HttpClientResponse; + +public class InboundWrapper implements InboundHeaders { + + private final HttpClientResponse response; + + public InboundWrapper(HttpClientResponse response) { + this.response = response; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public String getHeader(String name) { + return response.getHeader(name); + } + +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java new file mode 100644 index 0000000000..7f4765a262 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.OutboundHeaders; +import io.vertx.core.MultiMap; + +public class OutboundWrapper implements OutboundHeaders { + + private final MultiMap headers; + + public OutboundWrapper(MultiMap headers) { + this.headers = headers; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public void setHeader(String name, String value) { + headers.add(name, value); + } + +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java new file mode 100644 index 0000000000..2b57204455 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java @@ -0,0 +1,37 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Handler; +import io.vertx.core.Promise; + +public class PromiseHandlerWrapper implements Handler> { + + private Handler> original; + + private Token token; + + public PromiseHandlerWrapper(Handler> original, Token token) { + System.out.println("wrapped constr " + token); + this.original = original; + this.token = token; + } + + @Override + @Trace(async = true, excludeFromTransactionTrace = true) + public void handle(Promise event) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + this.original.handle(event); + this.original = null; + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java new file mode 100644 index 0000000000..e511bbcb17 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java @@ -0,0 +1,72 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.GenericParameters; +import com.newrelic.api.agent.HttpParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.weaver.Weaver; +import io.vertx.core.Handler; +import io.vertx.core.http.impl.HttpClientResponseImpl; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; + +public class VertxCoreUtil { + + private VertxCoreUtil() { + } + + private static final Map tokenMap = AgentBridge.collectionFactory.createConcurrentWeakKeyedMap(); + + public static final String VERTX_CLIENT = "Vertx-Client"; + public static final String END = "handleResponse"; + + private static final URI UNKNOWN_HOST_URI = URI.create("http://UnknownHost/"); + + public static void storeToken(Handler handler) { + if (handler != null && AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("storeToken --- "); + tokenMap.put(handler, NewRelic.getAgent().getTransaction().getToken()); + } + } + + public static void linkAndExpireToken(Handler handler) { + if (handler != null) { + final Token token = tokenMap.remove(handler); + if (token != null) { + token.linkAndExpire(); + } + } + } + + public static void processResponse(Segment segment, HttpClientResponseImpl resp, String host, int port, + String scheme) { + try { + URI uri = new URI(scheme, null, host, port, null, null, null); + segment.reportAsExternal(HttpParameters.library(VERTX_CLIENT) + .uri(uri) + .procedure(END) + .inboundHeaders(new InboundWrapper(resp)) + .build()); + } catch (URISyntaxException e) { + AgentBridge.instrumentation.noticeInstrumentationError(e, Weaver.getImplementationTitle()); + } + } + + public static void reportUnknownHost(Segment segment) { + segment.reportAsExternal(GenericParameters.library(VERTX_CLIENT) + .uri(UNKNOWN_HOST_URI) + .procedure(END) + .build()); + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java new file mode 100644 index 0000000000..cf996bf7a4 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -0,0 +1,107 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.http.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.Transaction; +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.vertx.instrumentation.AsyncHandlerWrapper; +import com.nr.vertx.instrumentation.OutboundWrapper; +import com.nr.vertx.instrumentation.VertxCoreUtil; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.MultiMap; +import io.vertx.core.Promise; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; + +import java.net.UnknownHostException; +import java.util.logging.Level; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +@Weave(type = MatchType.BaseClass, originalName = "io.vertx.core.http.impl.HttpClientRequestBase") +public abstract class HttpClientRequestBase_Instrumentation { + + @NewField + public Segment segment; + + @NewField + public Token token; + + public abstract MultiMap headers(); + + @Trace(async = true) + public HttpClientRequest response(Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response " + handler); + if (AgentBridge.getAgent().getTransaction(false) != null) { + this.token = NewRelic.getAgent().getTransaction().getToken(); + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + + return Weaver.callOriginal(); + } + + @Trace(async = true) + void handleResponse(Promise promise, HttpClientResponse resp, long timeoutMs) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse()"); + System.out.println("----------- linkAndExpire " + this.token.link() + " " + this.token); + if (segment != null) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse() segment != null"); + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + Token segmentToken = segment.getTransaction().getToken(); + System.out.println("segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segmentToken); + reportExternal(resp, segment); + + segment.end(); + System.out.println("2222222222222 link " + segmentToken.link()); + System.out.println("33333 expire " + segmentToken.expire()); + } + this.token.expire(); + this.token = null; + Transaction t1 = AgentBridge.getAgent().getTransaction(); + Transaction t2 = NewRelic.getAgent().getTransaction(); + + Weaver.callOriginal(); + } + + @Trace(async = true) + void handleException(Throwable t) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleException()"); + if (segment != null) { + if (t instanceof UnknownHostException) { + VertxCoreUtil.reportUnknownHost(segment); + } + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + } + Weaver.callOriginal(); + } + + private void reportExternal(HttpClientResponse response, Segment segment) { + if (response instanceof HttpClientResponseImpl) { + HttpClientResponseImpl resp = (HttpClientResponseImpl) response; + final String host = resp.request().getHost(); + final int port = resp.request().getPort(); + final String scheme = resp.request().ssl ? "https" : "http"; + VertxCoreUtil.processResponse(segment, resp, host, port, scheme); + } + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java new file mode 100644 index 0000000000..c0a9af31f7 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java @@ -0,0 +1,74 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.http.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.OutboundWrapper; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; + +import java.util.logging.Level; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +@Weave(originalName = "io.vertx.core.http.impl.HttpClientRequestImpl") +public abstract class HttpClientRequestImpl_Instrumentation extends HttpClientRequestBase_Instrumentation { + +// public Future end(Buffer chunk) { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk"); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk 2"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// } +// return Weaver.callOriginal(); +// } +// +// public void end(Buffer chunk, Handler> handler) { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler"); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler 2"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// } +// Weaver.callOriginal(); +// } +// +// public Future end() { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end()"); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end() 2"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// System.out.println("end() Segment -- " + segment); +// } +// return Weaver.callOriginal(); +// } +// +// public void end(Handler> handler) { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler)"); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler) 2"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// System.out.println("!!!!segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segment.getTransaction().getToken()); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// System.out.println("end(Handler> Segment -- " + segment); +// } +// Weaver.callOriginal(); +// } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java new file mode 100644 index 0000000000..c5630d9a81 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java @@ -0,0 +1,24 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package io.vertx.core.impl; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.AsyncHandlerWrapper; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; + +@Weave(originalName = "io.vertx.core.impl.AbstractContext") +abstract class AbstractContext_Instrumentation { + private static void setResultHandler(ContextInternal ctx, Future fut, Handler> resultHandler) { + System.out.println("setResulthandler ----"); + resultHandler = new AsyncHandlerWrapper<>(resultHandler, NewRelic.getAgent().getTransaction().getToken()); + Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java new file mode 100644 index 0000000000..500a580c3d --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java @@ -0,0 +1,25 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.impl; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.PromiseHandlerWrapper; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.Promise; + +@Weave(originalName = "io.vertx.core.impl.ContextImpl") +abstract class ContextImpl_Instrumentation { + static Future executeBlocking(ContextInternal context, Handler> blockingCodeHandler, WorkerPool workerPool, TaskQueue queue) { + System.out.println("executeBlocking ----"); + blockingCodeHandler = new PromiseHandlerWrapper(blockingCodeHandler, NewRelic.getAgent().getTransaction().getToken()); + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java new file mode 100644 index 0000000000..251b351b99 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java @@ -0,0 +1,84 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.impl.future; + +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.vertx.instrumentation.VertxCoreUtil; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; + +@Weave(originalName = "io.vertx.core.impl.future.FutureImpl") +abstract class FutureImpl_Instrumentation { + + @NewField + private Handler handler = null; + private Listener listener; + + public abstract boolean isComplete(); + + @Trace(async = true, excludeFromTransactionTrace = true) + public void addListener(Listener listener) { + if (isComplete()) { + VertxCoreUtil.linkAndExpireToken(listener); + } else { + VertxCoreUtil.storeToken(listener); + } +// if (!isComplete()) { +// System.out.println("addListener " + " " + this.listener + " " + listener + " " + System.identityHashCode(listener) + " " + System.currentTimeMillis()); +// assignAndStoreHandlerInstance(listener); +// } + Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public Future onSuccess(Handler handler) { + System.out.println("success"); + //assignAndStoreHandlerInstance(handler); + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public Future onComplete(Handler handler) { + System.out.println("complete"); + //assignAndStoreHandlerInstance(handler); + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public Future onFailure(Handler handler) { + //assignAndStoreHandlerInstance(handler); + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryComplete(Object result) { + System.out.println("trycomplete " + " " + listener + " " + System.identityHashCode(this) + " " + System.currentTimeMillis()); + VertxCoreUtil.linkAndExpireToken(this.listener); + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryFail(Throwable cause) { + System.out.println("tryFail"); + VertxCoreUtil.linkAndExpireToken(this.listener); + return Weaver.callOriginal(); + } + + private void assignAndStoreHandlerInstance(Listener listener) { + VertxCoreUtil.storeToken(listener); + } + +// private void assignAndStoreHandlerInstance(Handler listener) { +// VertxCoreUtil.storeToken(listener); +// } + +} diff --git a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java new file mode 100644 index 0000000000..0fa7ac126a --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java @@ -0,0 +1,59 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Vertx; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxBlockingTest { + + @Test + public void executeBlocking_withPromiseAndResult() throws InterruptedException { + Vertx vertx = Vertx.vertx(); + try { + executeBlocking(vertx); + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxBlockingTest/executeBlocking"; + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("InFuture")); + assertTrue(attributes.containsKey("InResponseHandler")); + } finally { + vertx.close(); + } + } + + @Trace(dispatcher = true) + void executeBlocking(Vertx vertx) throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + vertx.executeBlocking(future -> { + NewRelic.addCustomParameter("InFuture", "yes"); + future.complete(); + }, res -> { + NewRelic.addCustomParameter("InResponseHandler", "yes"); + countDownLatch.countDown(); + }); + countDownLatch.await(); + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java new file mode 100644 index 0000000000..2aaa25e9d1 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java @@ -0,0 +1,379 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.CatHelper; +import com.newrelic.agent.introspec.ExternalRequest; +import com.newrelic.agent.introspec.HttpTestServer; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.MetricsHelper; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.agent.introspec.internal.HttpServerLocator; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.Collection; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxClient { + + private static int port; + private static Future server; + private static Vertx vertx; + + @BeforeClass + public static void beforeClass() { + port = getAvailablePort(); + vertx = Vertx.vertx(); + + server = vertx.createHttpServer().requestHandler(request -> { + final String statusCode = request.getHeader("statusCode"); + if (statusCode == null) { + System.out.println("statusCode is null"); + request.response().end("response"); + } else { + System.out.println("statusCode is NOT null -- " + statusCode); + if (request.absoluteURI().equals("/redirect")) { + request.headers().clear(); + request.response().putHeader("Location", "http://localhost:" + port + "/other"); + } + request.response().setStatusCode(Integer.parseInt(statusCode)).end("response"); + } + }).listen(port); + } + + @AfterClass + public static void afterClass() { + server.result().close(); + vertx.close(); + } + + @Test + public void testGet() throws InterruptedException { + getCall(); + // Wait for transaction to finish + System.out.println("testGet " + NewRelic.getAgent().getTransaction()); + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall", "localhost"); + } + + @Trace(dispatcher = true) + public void getCall() throws InterruptedException { + System.out.println("dispatcher -- " + NewRelic.getAgent().getTransaction()); + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + //httpClient.close(); + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request, waiting for response in ar2 + if (respAsyncResult.succeeded()) { + System.out.println("respAsyncResult succeed"); + HttpClientResponse response = respAsyncResult.result(); + int statusCode = response.statusCode(); + latch.countDown(); + System.out.println("response: " + response + " " + statusCode); + response.body(respBufferAsyncResult -> { //Retriev response + if (respBufferAsyncResult.succeeded()) { + Buffer body = respBufferAsyncResult.result(); + // Handle body entirely + } else { + // Handle server error, for example, connection closed + } + }); + } else { + // Handle server error, for example, connection closed + } + }); + } else { + // Connection error, for example, invalid server or invalid SSL certificate + } + }); + latch.await(); + + System.out.println("testGet " + NewRelic.getAgent().getTransaction()); + } + + @Test + public void testGetNow() throws InterruptedException { + getNowCall(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getNowCall", "localhost"); + } + + @Trace(dispatcher = true) + private void getNowCall() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + //httpClient.getNow(port, "localhost", "/", requestHandler(latch)); + latch.await(); + } + + @Test + public void testPost() throws InterruptedException { + postCall(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/postCall", "localhost"); + } + + @Trace(dispatcher = true) + private void postCall() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + //httpClient.post(port, "localhost", "/", requestHandler(latch)).end(); + latch.await(); + } + + @Test + public void testEndMethods() throws InterruptedException { + endMethods(); + assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); + final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( + "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/endMethods").iterator().next(); + assertEquals(4, externalRequest.getCount()); + assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + @Trace(dispatcher = true) + public void endMethods() throws InterruptedException { + HttpClient httpClient = vertx.createHttpClient(); + CountDownLatch latch = new CountDownLatch(4); + + Buffer bufferChunk = Buffer.buffer("buffer chunk!"); + String stringChunk = "string chunk!"; + String encoding = "UTF-8"; + + // tests the various overloaded versions of the end method + //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); + //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(bufferChunk); + //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk, encoding); + //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk); + latch.await(); + } + + @Test + public void testMethods() throws InterruptedException { + requestMethods(); + assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); + final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( + "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/requestMethods").iterator().next(); + assertEquals(5, externalRequest.getCount()); + assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + @Trace(dispatcher = true) + public void requestMethods() throws InterruptedException { + HttpClient httpClient = vertx.createHttpClient(); + CountDownLatch latch = new CountDownLatch(5); + //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); + //httpClient.request(HttpMethod.POST, port, "localhost", "/hi", requestHandler(latch)).end(); + //httpClient.request(HttpMethod.PUT, port, "localhost", "/hi", requestHandler(latch)).end(); + //httpClient.request(HttpMethod.HEAD, port, "localhost", "/hi", requestHandler(latch)).end(); + //httpClient.request(HttpMethod.DELETE, port, "localhost", "/hi", requestHandler(latch)).end(); + latch.await(); + } + + @Test + public void testRedirect() throws InterruptedException { + redirect(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/redirect", "localhost"); + } + + @Trace(dispatcher = true) + public void redirect() throws InterruptedException { + HttpClient httpClient = vertx.createHttpClient(); + CountDownLatch latch = new CountDownLatch(1); + //httpClient.get(port, "localhost", "/redirect") + // .putHeader("statusCode", "301") + // .setFollowRedirects(true) + // .handler(requestHandler(latch)) + // .end(); + latch.await(); + } + + @Test + public void testCat() throws Exception { + try (HttpTestServer httpServer = HttpServerLocator.createAndStart()) { + cat(httpServer); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + String host = httpServer.getEndPoint().getHost(); + String txName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/cat"; + assertEquals(2, introspector.getFinishedTransactionCount(250)); + Collection names = introspector.getTransactionNames(); + assertEquals(2, names.size()); + assertTrue(names.contains(httpServer.getServerTransactionName())); + assertTrue(names.contains(txName)); + + // scoped metrics + assertEquals(1, MetricsHelper.getScopedMetricCount(txName, "ExternalTransaction/" + host + "/" + + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); + assertEquals(1, MetricsHelper.getScopedMetricCount(txName, + "Java/com.nr.vertx.instrumentation.VertxClient/cat")); + + // unscoped metrics + assertEquals(1, MetricsHelper.getUnscopedMetricCount("ExternalTransaction/" + host + "/" + + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/" + host + "/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); + + // events + Collection transactionEvents = introspector.getTransactionEvents(txName); + assertEquals(1, transactionEvents.size()); + TransactionEvent transactionEvent = transactionEvents.iterator().next(); + assertEquals(1, transactionEvent.getExternalCallCount()); + assertTrue(transactionEvent.getExternalDurationInSec() > 0); + + CatHelper.verifyOneSuccessfulCat(introspector, txName); + + // external request information + Collection externalRequests = introspector.getExternalRequests(txName); + assertEquals(1, externalRequests.size()); + ExternalRequest externalRequest = externalRequests.iterator().next(); + assertEquals(1, externalRequest.getCount()); + assertEquals(host, externalRequest.getHostname()); + } + } + + @Trace(dispatcher = true) + public void cat(HttpTestServer httpServer) throws Exception { + try { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + //httpClient.get(httpServer.getEndPoint().getPort(), httpServer.getEndPoint().getHost(), "/") + //.putHeader(HttpTestServer.DO_CAT, "true") + // .handler(requestHandler(latch)) + // .end(); + latch.await(); + } finally { + httpServer.shutdown(); + } + } + + @Test + public void testUnknownHost() throws Exception { + unknownHost(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(250)); + + final String txn = introspector.getTransactionNames().iterator().next(); + assertNotNull("Transaction not found", txn); + + assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/end")); + + // Unknown hosts generate no external rollups + assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/all")); + + // Make sure exception handler is linked + final TransactionEvent event = introspector.getTransactionEvents(txn).iterator().next(); + assertTrue(event.getAttributes().containsKey("exceptionHandler")); + } + + @Trace(dispatcher = true) + private void unknownHost() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + //httpClient.get(port, "notARealHostDuderina.com", "/") + // .exceptionHandler(exceptionHandler(latch)) + // .handler(requestHandler(null)) + // .end(); + latch.await(); + } + + private Handler exceptionHandler(CountDownLatch latch) { + return error -> { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + }; + } + + private Handler requestHandler(CountDownLatch latch) { + return response -> { + NewRelic.addCustomParameter("responseHandler", "true"); + response.bodyHandler(body -> { + NewRelic.addCustomParameter("bodyHandler", "true"); + latch.countDown(); + }); + }; + } + + public void assertExternal(String transactionName, String host) { + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Collection externalRequests = introspector.getExternalRequests(transactionName); + ExternalRequest request = externalRequests.iterator().next(); + assertEquals(host, request.getHostname()); + assertEquals("Vertx-Client", request.getLibrary()); + assertEquals("end", request.getOperation()); + Collection events = introspector.getTransactionEvents(transactionName); + TransactionEvent event = events.iterator().next(); + assertTrue(event.getAttributes().containsKey("responseHandler")); + + assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/end")); + + Collection transactionEvents = introspector.getTransactionEvents(transactionName); + assertEquals(1, transactionEvents.size()); + TransactionEvent transactionEvent = transactionEvents.iterator().next(); + assertEquals(1, transactionEvent.getExternalCallCount()); + assertTrue(transactionEvent.getExternalDurationInSec() > 0); + + // external rollups + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + private static int getAvailablePort() { + int port; + + try { + ServerSocket socket = new ServerSocket(0); + port = socket.getLocalPort(); + socket.close(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral port"); + } + return port; + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java new file mode 100644 index 0000000000..f3676b8fd0 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java @@ -0,0 +1,200 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.CompositeFuture; +import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxFuture { + + @Test + public void testCompositeFuture() throws InterruptedException { + compositeFuturesAllFuturesSucceed(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFuturesAllFuturesSucceed"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("compositeFuture")); + } + + @Trace(dispatcher = true) + private void compositeFuturesAllFuturesSucceed() throws InterruptedException { + Vertx vertx = Vertx.vertx(); + CountDownLatch latch = new CountDownLatch(1); + Promise promise1 = Promise.promise(); + Promise promise2 = Promise.promise(); + + CompositeFuture.all(promise1.future(), promise2.future()).onComplete((ar -> { + if (ar.succeeded()) { + NewRelic.addCustomParameter("compositeFuture", "yes"); + } + latch.countDown(); + })); + + vertx.setTimer(1, handler -> { + promise1.complete("promise1"); + promise2.complete("promise2"); + }); + + latch.await(); + } + + @Test + public void whenFutureFails_withThrowable_txnStillCompletes() throws InterruptedException { + failFutureWithThrowable(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithThrowable"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureFails_withString_txnStillCompletes() throws InterruptedException { + failFutureWithString(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithString"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureCompletes_txnStillCompletes() throws InterruptedException { + completeFutureSuccessfully(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfully"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureCompletes_withOnCompleteRegistered_txnStillCompletes() throws InterruptedException { + completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Trace(dispatcher = true) + private void failFutureWithString() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onFailure(ar -> { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.fail("oops"); + }); + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void failFutureWithThrowable() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onFailure(ar -> { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.fail(new IllegalArgumentException("foo")); + }); + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void completeFutureSuccessfully() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onSuccess(ar -> { + NewRelic.addCustomParameter("future", "success"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.complete("hooray"); + }); + + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onComplete(ar -> { + NewRelic.addCustomParameter("future", "onComplete"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.complete("hooray"); + }); + + countDownLatch.await(); + } +} diff --git a/instrumentation/vertx-core-4.3.2/.gitignore b/instrumentation/vertx-core-4.3.2/.gitignore new file mode 100644 index 0000000000..b63da4551b --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/instrumentation/vertx-core-4.3.2/build.gradle b/instrumentation/vertx-core-4.3.2/build.gradle new file mode 100644 index 0000000000..d3bd7ec4a5 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/build.gradle @@ -0,0 +1,25 @@ +jar { + manifest { + attributes 'Implementation-Title': 'com.newrelic.instrumentation.vertx-core-4.3.2' + } +} + + + +dependencies { + implementation(project(":agent-bridge")) + implementation("io.vertx:vertx-core:4.3.2") + testImplementation("io.vertx:vertx-core:4.0.0") +} + +verifyInstrumentation { + passesOnly 'io.vertx:vertx-core:[4.3.2,)' + excludeRegex '.*CR[0-9]*' + excludeRegex '.*-milestone[0-9]' + excludeRegex '.*Beta[0-9]' +} + +site { + title 'Vertx' + type 'Framework' +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java new file mode 100644 index 0000000000..e7289a87b0 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.InboundHeaders; +import io.vertx.core.http.HttpClientResponse; + +public class InboundWrapper implements InboundHeaders { + + private final HttpClientResponse response; + + public InboundWrapper(HttpClientResponse response) { + this.response = response; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public String getHeader(String name) { + return response.getHeader(name); + } + +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java new file mode 100644 index 0000000000..0bc00583ca --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.OutboundHeaders; +import io.vertx.core.MultiMap; + +public class OutboundWrapper implements OutboundHeaders { + + private final MultiMap headers; + + public OutboundWrapper(MultiMap headers) { + this.headers = headers; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public void setHeader(String name, String value) { + headers.add(name, value); + } + +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java new file mode 100644 index 0000000000..b48dd349ae --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java @@ -0,0 +1,71 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.GenericParameters; +import com.newrelic.api.agent.HttpParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.weaver.Weaver; +import io.vertx.core.Handler; +import io.vertx.core.http.impl.HttpClientResponseImpl; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; + +public class VertxCoreUtil { + + private VertxCoreUtil() { + } + + private static final Map tokenMap = AgentBridge.collectionFactory.createConcurrentWeakKeyedMap(); + + public static final String VERTX_CLIENT = "Vertx-Client"; + public static final String END = "end"; + + private static URI UNKNOWN_HOST_URI = URI.create("http://UnknownHost/"); + + public static void storeToken(Handler handler) { + if (handler != null && AgentBridge.getAgent().getTransaction(false) != null) { + tokenMap.put(handler, NewRelic.getAgent().getTransaction().getToken()); + } + } + + public static void linkAndExpireToken(Handler handler) { + if (handler != null) { + final Token token = tokenMap.remove(handler); + if (token != null) { + token.linkAndExpire(); + } + } + } + + public static void processResponse(Segment segment, HttpClientResponseImpl resp, String host, int port, + String scheme) { + try { + URI uri = new URI(scheme, null, host, port, null, null, null); + segment.reportAsExternal(HttpParameters.library(VERTX_CLIENT) + .uri(uri) + .procedure(END) + .inboundHeaders(new InboundWrapper(resp)) + .build()); + } catch (URISyntaxException e) { + AgentBridge.instrumentation.noticeInstrumentationError(e, Weaver.getImplementationTitle()); + } + } + + public static void reportUnknownHost(Segment segment) { + segment.reportAsExternal(GenericParameters.library(VERTX_CLIENT) + .uri(UNKNOWN_HOST_URI) + .procedure(END) + .build()); + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java new file mode 100644 index 0000000000..3cb00268f0 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -0,0 +1,64 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.http.impl; + +import com.newrelic.api.agent.Segment; +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.vertx.instrumentation.VertxCoreUtil; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpClientResponse; + +import java.net.UnknownHostException; + +@Weave(originalName = "io.vertx.core.http.impl.HttpClientRequestBase") +public abstract class HttpClientRequestBase_Instrumentation { + + @NewField + public Segment segment; + + public abstract MultiMap headers(); + + @Trace(async = true) + void handleResponse(HttpClientResponse resp) { + if (segment != null) { + reportExternal(resp, segment); + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + } + + Weaver.callOriginal(); + } + + @Trace(async = true) + void handleException(Throwable t) { + if (segment != null) { + if (t instanceof UnknownHostException) { + VertxCoreUtil.reportUnknownHost(segment); + } + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + } + Weaver.callOriginal(); + } + + private void reportExternal(HttpClientResponse response, Segment segment) { + if (response instanceof HttpClientResponseImpl) { + HttpClientResponseImpl resp = (HttpClientResponseImpl) response; + final String host = resp.request().getHost(); + final int port = resp.request().getPort(); + final String scheme = resp.request().ssl ? "https" : "http"; + VertxCoreUtil.processResponse(segment, resp, host, port, scheme); + } + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java new file mode 100644 index 0000000000..54b6612f56 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java @@ -0,0 +1,64 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.http.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.OutboundWrapper; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +@Weave(originalName = "io.vertx.core.http.impl.HttpClientRequestImpl") +public abstract class HttpClientRequestImpl_Instrumentation extends HttpClientRequestBase_Instrumentation { + + public Future end(Buffer chunk) { + if (AgentBridge.getAgent().getTransaction(false) != null) { + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + Weaver.callOriginal(); + + //hack + return null; + } + + public void end(Buffer chunk, Handler> handler) { + if (AgentBridge.getAgent().getTransaction(false) != null) { + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + Weaver.callOriginal(); + } + + public Future end() { + if (AgentBridge.getAgent().getTransaction(false) != null) { + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + Weaver.callOriginal(); + + //hack + return null; + } + + public void end(Handler> handler) { + if (AgentBridge.getAgent().getTransaction(false) != null) { + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + Weaver.callOriginal(); + } + +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java new file mode 100644 index 0000000000..f896ac0976 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java @@ -0,0 +1,36 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.impl; + +import com.newrelic.api.agent.weaver.Weave; + +@Weave(originalName = "io.vertx.core.impl.ContextBase") +public abstract class ContextBase_Instrumentation { + + //static Future executeBlocking(ContextInternal context, Handler> blockingCodeHandler, WorkerPool workerPool, TaskQueue queue) + +// void executeBlocking(Handler> blockingCodeHandler, Handler resultHandler, +// Executor exec, TaskQueue queue, PoolMetrics metrics) { +// VertxCoreUtil.storeToken(blockingCodeHandler); +// VertxCoreUtil.storeToken(resultHandler); +// Weaver.callOriginal(); +// } +// +// @Trace(async = true) +// private void lambda$executeBlocking$2(PoolMetrics metricsHandler, Object object, Handler handler, Handler resultHandler) { +// VertxCoreUtil.linkAndExpireToken(handler); +// Weaver.callOriginal(); +// } +// +// @Trace(async = true) +// private static void lambda$null$0(Handler handler, AsyncResult result, Void v) { +// VertxCoreUtil.linkAndExpireToken(handler); +// Weaver.callOriginal(); +// } + +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java new file mode 100644 index 0000000000..9266fcee33 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java @@ -0,0 +1,48 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.impl; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.VertxCoreUtil; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; + +@Weave(originalName = "io.vertx.core.impl.future.FutureImpl") +abstract class FutureImpl_Instrumentation { + + //private Handler handler = Weaver.callOriginal(); + + //to use concrete method in instrumented class + public abstract boolean isComplete(); + + + @Trace(async = true, excludeFromTransactionTrace = true) + public Future onComplete(Handler handler) { + if (isComplete()) { + VertxCoreUtil.linkAndExpireToken(handler); + } else { + VertxCoreUtil.storeToken(handler); + } + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryComplete(Object result) { + //VertxCoreUtil.linkAndExpireToken(handler); + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryFail(Throwable cause) { + //VertxCoreUtil.linkAndExpireToken(handler); + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java new file mode 100644 index 0000000000..622d18e349 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java @@ -0,0 +1,60 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Vertx; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxBlockingTest { + + @Test + public void testBlocking() throws InterruptedException { + Vertx vertx = Vertx.vertx(); + try { + executeBlocking(vertx); + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxBlockingTest/executeBlocking"; + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("InFuture")); + assertTrue(attributes.containsKey("InResponseHandler")); + } finally { + vertx.close(); + } + } + + @Trace(dispatcher = true) + void executeBlocking(Vertx vertx) throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + vertx.executeBlocking(future -> { + NewRelic.addCustomParameter("InFuture", "yes"); + future.complete(); + }, res -> { + NewRelic.addCustomParameter("InResponseHandler", "yes"); + countDownLatch.countDown(); + }); + countDownLatch.await(); + } + +} diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java new file mode 100644 index 0000000000..9e48e5905c --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java @@ -0,0 +1,348 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.CatHelper; +import com.newrelic.agent.introspec.ExternalRequest; +import com.newrelic.agent.introspec.HttpTestServer; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.MetricsHelper; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.agent.introspec.internal.HttpServerLocator; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.Collection; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxClient { + +/* + private static int port; + private static HttpServer server; + private static Vertx vertx; + + @BeforeClass + public static void beforeClass() { + port = getAvailablePort(); + vertx = Vertx.vertx(); + server = vertx.createHttpServer().requestHandler(request -> { + final String statusCode = request.getHeader("statusCode"); + if (statusCode == null) { + request.response().end("response"); + } else { + if (request.absoluteURI().equals("/redirect")) { + request.headers().clear(); + request.response().putHeader("Location", "http://localhost:" + port + "/other"); + } + request.response().setStatusCode(Integer.parseInt(statusCode)).end("response"); + } + }).listen(port); + } + + @AfterClass + public static void afterClass() { + server.close(); + vertx.close(); + } + + @Test + public void testGet() throws InterruptedException { + getCall(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall", "localhost"); + } + + @Trace(dispatcher = true) + public void getCall() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + HttpClientRequest request = httpClient.options(port, "localhost", "/").handler(requestHandler(latch)); + request.end(); + latch.await(); + } + + @Test + public void testGetNow() throws InterruptedException { + getNowCall(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getNowCall", "localhost"); + } + + @Trace(dispatcher = true) + private void getNowCall() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.getNow(port, "localhost", "/", requestHandler(latch)); + latch.await(); + } + + @Test + public void testPost() throws InterruptedException { + postCall(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/postCall", "localhost"); + } + + @Trace(dispatcher = true) + private void postCall() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.post(port, "localhost", "/", requestHandler(latch)).end(); + latch.await(); + } + + @Test + public void testEndMethods() throws InterruptedException { + endMethods(); + assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); + final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( + "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/endMethods").iterator().next(); + assertEquals(4, externalRequest.getCount()); + assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + @Trace(dispatcher = true) + public void endMethods() throws InterruptedException { + HttpClient httpClient = vertx.createHttpClient(); + CountDownLatch latch = new CountDownLatch(4); + + Buffer bufferChunk = Buffer.buffer("buffer chunk!"); + String stringChunk = "string chunk!"; + String encoding = "UTF-8"; + + // tests the various overloaded versions of the end method + httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(bufferChunk); + httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk, encoding); + httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk); + latch.await(); + } + + @Test + public void testMethods() throws InterruptedException { + requestMethods(); + assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); + final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( + "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/requestMethods").iterator().next(); + assertEquals(5, externalRequest.getCount()); + assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + @Trace(dispatcher = true) + public void requestMethods() throws InterruptedException { + HttpClient httpClient = vertx.createHttpClient(); + CountDownLatch latch = new CountDownLatch(5); + httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.POST, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.PUT, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.HEAD, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.DELETE, port, "localhost", "/hi", requestHandler(latch)).end(); + latch.await(); + } + + @Test + public void testRedirect() throws InterruptedException { + redirect(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/redirect", "localhost"); + } + + @Trace(dispatcher = true) + public void redirect() throws InterruptedException { + HttpClient httpClient = vertx.createHttpClient(); + CountDownLatch latch = new CountDownLatch(1); + httpClient.get(port, "localhost", "/redirect") + .putHeader("statusCode", "301") + .setFollowRedirects(true) + .handler(requestHandler(latch)) + .end(); + latch.await(); + } + + @Test + public void testCat() throws Exception { + try (HttpTestServer httpServer = HttpServerLocator.createAndStart()) { + cat(httpServer); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + String host = httpServer.getEndPoint().getHost(); + String txName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/cat"; + assertEquals(2, introspector.getFinishedTransactionCount(250)); + Collection names = introspector.getTransactionNames(); + assertEquals(2, names.size()); + assertTrue(names.contains(httpServer.getServerTransactionName())); + assertTrue(names.contains(txName)); + + // scoped metrics + assertEquals(1, MetricsHelper.getScopedMetricCount(txName, "ExternalTransaction/" + host + "/" + + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); + assertEquals(1, MetricsHelper.getScopedMetricCount(txName, + "Java/com.nr.vertx.instrumentation.VertxClient/cat")); + + // unscoped metrics + assertEquals(1, MetricsHelper.getUnscopedMetricCount("ExternalTransaction/" + host + "/" + + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/" + host + "/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); + + // events + Collection transactionEvents = introspector.getTransactionEvents(txName); + assertEquals(1, transactionEvents.size()); + TransactionEvent transactionEvent = transactionEvents.iterator().next(); + assertEquals(1, transactionEvent.getExternalCallCount()); + assertTrue(transactionEvent.getExternalDurationInSec() > 0); + + CatHelper.verifyOneSuccessfulCat(introspector, txName); + + // external request information + Collection externalRequests = introspector.getExternalRequests(txName); + assertEquals(1, externalRequests.size()); + ExternalRequest externalRequest = externalRequests.iterator().next(); + assertEquals(1, externalRequest.getCount()); + assertEquals(host, externalRequest.getHostname()); + } + } + + @Trace(dispatcher = true) + public void cat(HttpTestServer httpServer) throws Exception { + try { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.get(httpServer.getEndPoint().getPort(), httpServer.getEndPoint().getHost(), "/") + .putHeader(HttpTestServer.DO_CAT, "true") + .handler(requestHandler(latch)) + .end(); + latch.await(); + } finally { + httpServer.shutdown(); + } + } + + @Test + public void testUnknownHost() throws Exception { + unknownHost(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(250)); + + final String txn = introspector.getTransactionNames().iterator().next(); + assertNotNull("Transaction not found", txn); + + assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/end")); + + // Unknown hosts generate no external rollups + assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/all")); + + // Make sure exception handler is linked + final TransactionEvent event = introspector.getTransactionEvents(txn).iterator().next(); + assertTrue(event.getAttributes().containsKey("exceptionHandler")); + } + + @Trace(dispatcher = true) + private void unknownHost() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.get(port, "notARealHostDuderina.com", "/") + .exceptionHandler(exceptionHandler(latch)) + .handler(requestHandler(null)) + .end(); + latch.await(); + } + + private Handler exceptionHandler(CountDownLatch latch) { + return error -> { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + }; + } + + private Handler requestHandler(CountDownLatch latch) { + return response -> { + NewRelic.addCustomParameter("responseHandler", "true"); + response.bodyHandler(body -> { + NewRelic.addCustomParameter("bodyHandler", "true"); + latch.countDown(); + }); + }; + } + + public void assertExternal(String transactionName, String host) { + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Collection externalRequests = introspector.getExternalRequests(transactionName); + ExternalRequest request = externalRequests.iterator().next(); + assertEquals(host, request.getHostname()); + assertEquals("Vertx-Client", request.getLibrary()); + assertEquals("end", request.getOperation()); + Collection events = introspector.getTransactionEvents(transactionName); + TransactionEvent event = events.iterator().next(); + assertTrue(event.getAttributes().containsKey("responseHandler")); + + assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/end")); + + Collection transactionEvents = introspector.getTransactionEvents(transactionName); + assertEquals(1, transactionEvents.size()); + TransactionEvent transactionEvent = transactionEvents.iterator().next(); + assertEquals(1, transactionEvent.getExternalCallCount()); + assertTrue(transactionEvent.getExternalDurationInSec() > 0); + + // external rollups + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + private static int getAvailablePort() { + int port; + + try { + ServerSocket socket = new ServerSocket(0); + port = socket.getLocalPort(); + socket.close(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral port"); + } + return port; + } +*/ + +} diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java new file mode 100644 index 0000000000..831f9e01d1 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java @@ -0,0 +1,105 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.CompositeFuture; +import io.vertx.core.Future; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxFuture { + + @Test + public void testCompositeFuture() throws InterruptedException { + compositeFutures(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFutures"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("success")); + } + + @Trace(dispatcher = true) + private void compositeFutures() throws InterruptedException { + final Future abc = Future.future(); + final Future def = Future.future(); + + final ExecutorService service = Executors.newSingleThreadExecutor(); + + CountDownLatch latch = new CountDownLatch(1); + CompositeFuture.all(abc, def).setHandler(result -> { + if (result.succeeded()) { + NewRelic.addCustomParameter("success", "yes"); + } + latch.countDown(); + }); + + service.submit(() -> { + abc.complete("abc"); + }); + + service.submit(() -> { + def.complete("def"); + }); + + latch.await(); + } + + @Test + public void testFutureFail() throws InterruptedException { + failFuture(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFuture"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Trace(dispatcher = true) + private void failFuture() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Future future = Future.future(); + future.setHandler(ar -> { + if (ar.failed()) { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); + } + }); + + ExecutorService service = Executors.newSingleThreadExecutor(); + service.submit(() -> { + future.fail(new RuntimeException()); + }); + + countDownLatch.await(); + } + +} diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/Segment.java b/newrelic-agent/src/main/java/com/newrelic/agent/Segment.java index bbb0d91392..a4116f02bb 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/Segment.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/Segment.java @@ -187,6 +187,8 @@ public void finish(final Throwable t) { private void finish(final Throwable t, boolean async) { if (!isFinished.getAndSet(true)) { + System.out.println("finish " + this); + markFinishTime(); final Tracer tracer = parent; diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java index 7aabd7a552..1fe9616a7e 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java @@ -58,6 +58,7 @@ public Tracer getInitiatingTracer() { @Override public boolean expire() { + System.out.println("expire --- " + this); if (active.compareAndSet(Boolean.TRUE, Boolean.FALSE)) { Transaction tx = getTransaction().getTransactionIfExists(); if (tx != null) { diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java b/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java index 1b6af54e83..ff250212a2 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java @@ -2209,6 +2209,7 @@ public Token getToken() { getMetricAggregator().incrementCounter(AgentBridge.currentApiSource.get().getSupportabilityMetric( MetricNames.SUPPORTABILITY_API_TOKEN)); + System.out.println("getToken --- " + token); return token; } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/TransactionApiImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/TransactionApiImpl.java index e6158640f2..c5054a3494 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/TransactionApiImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/TransactionApiImpl.java @@ -468,6 +468,9 @@ public Segment startSegment(String category, String segmentName) { } Segment segment = tx.startSegment(category, segmentName); + + System.out.println("startSegment " + segment); + return segment == null ? NoOpSegment.INSTANCE : segment; } diff --git a/settings.gradle b/settings.gradle index fd11d9eb3c..4b0412c6fe 100644 --- a/settings.gradle +++ b/settings.gradle @@ -379,5 +379,7 @@ include 'instrumentation:vertx-core-3.4.1' include 'instrumentation:vertx-core-3.6.0' include 'instrumentation:vertx-core-3.8.0' include 'instrumentation:vertx-core-3.9.0' +include 'instrumentation:vertx-core-4.0.0' +include 'instrumentation:vertx-core-4.3.2' include 'instrumentation:zio' From 42d6ba460a41b8c2d9a2730ac333842544d6a721 Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Wed, 1 Nov 2023 16:04:02 -0400 Subject: [PATCH 02/16] WIP commit - 1st pass working --- instrumentation/vertx-core-4.0.0/README.md | 42 +++ .../instrumentation/AsyncHandlerWrapper.java | 2 - .../HttpClientRequestPromiseWrapper.java | 153 +++++++++++ .../PromiseHandlerWrapper.java | 1 - .../vertx/instrumentation/VertxCoreUtil.java | 2 +- .../impl/HttpClientImpl_Instrumentation.java | 31 +++ ...HttpClientRequestBase_Instrumentation.java | 50 ++-- ...HttpClientRequestImpl_Instrumentation.java | 80 +++--- .../impl/AbstractContext_Instrumentation.java | 8 +- .../impl/ContextImpl_Instrumentation.java | 8 +- .../future/FutureImpl_Instrumentation.java | 41 --- .../nr/vertx/instrumentation/VertxClient.java | 254 +++++++++--------- 12 files changed, 424 insertions(+), 248 deletions(-) create mode 100644 instrumentation/vertx-core-4.0.0/README.md create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java diff --git a/instrumentation/vertx-core-4.0.0/README.md b/instrumentation/vertx-core-4.0.0/README.md new file mode 100644 index 0000000000..36cd1a1c4d --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/README.md @@ -0,0 +1,42 @@ +Vert.x 4.00 Core Instrumentation +================================ + +### HTTP Client + +The Vert.x core instrumentation module is mainly concerned with instrumenting the low level Vert.x HTTP Client and associated Future instances. + +The instrumentation module can instrument client instances created various ways. For example, via inline callbacks: +```text + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.POST, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + HttpClientResponse response = respAsyncResult.result(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + // Handle body + } + }); + } + }); + } + }); +``` + +Or clients created that utilize Futures for response handling: +```text + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port, "localhost", "/", ar -> { + if (ar.succeeded()) { + HttpClientRequest request = ar.result(); + request.send("foo") + .onSuccess(response -> { + //Success handler + }).onFailure(err -> { + //Failure handler + }); + } + }); +``` diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java index 3a31ccd31c..00f88bfb4d 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java @@ -17,7 +17,6 @@ public class AsyncHandlerWrapper implements Handler> { private Token token; public AsyncHandlerWrapper(Handler> original, Token token) { - System.out.println("------- Construct AsyncHandlerWrapper "+ token); this.original = original; this.token = token; } @@ -26,7 +25,6 @@ public AsyncHandlerWrapper(Handler> original, Token token) { @Trace(async = true, excludeFromTransactionTrace = true) public void handle(AsyncResult event) { if (token != null) { - System.out.println("------- Link&Expire " + this.token); token.linkAndExpire(); token = null; } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java new file mode 100644 index 0000000000..f567a17e84 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java @@ -0,0 +1,153 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.Transaction; +import io.netty.util.concurrent.Future; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.impl.ContextInternal; +import io.vertx.core.impl.future.Listener; +import io.vertx.core.impl.future.PromiseInternal; + +import java.util.function.Function; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +public class HttpClientRequestPromiseWrapper implements PromiseInternal { + + private final PromiseInternal original; + + private Token token; + + public HttpClientRequestPromiseWrapper(PromiseInternal original, Token token) { + this.original = original; + this.token = token; + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public void complete(HttpClientRequest req) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + original.complete(req); + } + + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryFail(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (t.toString().contains("UnknownHostException") && txn != null) { + Segment segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + VertxCoreUtil.reportUnknownHost(segment); + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } + + return original.tryFail(t); + } + + @Override + public boolean tryComplete(HttpClientRequest result) { + return original.tryComplete(result); + } + + @Override + public io.vertx.core.Future future() { + return original.future(); + } + + public ContextInternal context() { + return original.context(); + } + + @Override + public void addListener(Listener listener) { + original.addListener(listener); + } + + @Override + public void operationComplete(Future future) throws Exception { + original.operationComplete(future); + } + + @Override + public boolean isComplete() { + return original.isComplete(); + } + + @Override + public io.vertx.core.Future onComplete(Handler> handler) { + return original.onComplete(handler); + } + + @Override + public HttpClientRequest result() { + return original.result(); + } + + @Override + public Throwable cause() { + return original.cause(); + } + + @Override + public boolean succeeded() { + return original.succeeded(); + } + + @Override + public boolean failed() { + return original.failed(); + } + + @Override + public io.vertx.core.Future compose(Function> successMapper, + Function> failureMapper) { + return original.compose(successMapper, failureMapper); + } + + @Override + public io.vertx.core.Future eventually(Function> mapper) { + return original.eventually(mapper); + } + + @Override + public io.vertx.core.Future map(Function mapper) { + return original.map(mapper); + } + + @Override + public io.vertx.core.Future map(V value) { + return original.map(value); + } + + @Override + public io.vertx.core.Future otherwise(Function mapper) { + return original.otherwise(mapper); + } + + @Override + public io.vertx.core.Future otherwise(HttpClientRequest value) { + return original.otherwise(value); + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java index 2b57204455..0749efd318 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java @@ -18,7 +18,6 @@ public class PromiseHandlerWrapper implements Handler> { private Token token; public PromiseHandlerWrapper(Handler> original, Token token) { - System.out.println("wrapped constr " + token); this.original = original; this.token = token; } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java index e511bbcb17..771d7aebef 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java @@ -13,6 +13,7 @@ import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.Segment; import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Transaction; import com.newrelic.api.agent.weaver.Weaver; import io.vertx.core.Handler; import io.vertx.core.http.impl.HttpClientResponseImpl; @@ -35,7 +36,6 @@ private VertxCoreUtil() { public static void storeToken(Handler handler) { if (handler != null && AgentBridge.getAgent().getTransaction(false) != null) { - System.out.println("storeToken --- "); tokenMap.put(handler, NewRelic.getAgent().getTransaction().getToken()); } } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java new file mode 100644 index 0000000000..e5288565ac --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java @@ -0,0 +1,31 @@ +package io.vertx.core.http.impl; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.HttpClientRequestPromiseWrapper; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.impl.future.PromiseInternal; +import io.vertx.core.net.SocketAddress; + +@Weave(type = MatchType.BaseClass, originalName = "io.vertx.core.http.impl.HttpClientImpl") +public class HttpClientImpl_Instrumentation { + private void request( + HttpMethod method, + SocketAddress peerAddress, + SocketAddress server, + String host, + int port, + Boolean useSSL, + String requestURI, + MultiMap headers, + long timeout, + Boolean followRedirects, + PromiseInternal requestPromise) { + requestPromise = new HttpClientRequestPromiseWrapper(requestPromise, NewRelic.getAgent().getTransaction().getToken()); + Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java index cf996bf7a4..a59e0a2c74 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -39,45 +39,35 @@ public abstract class HttpClientRequestBase_Instrumentation { @NewField public Segment segment; - @NewField - public Token token; - public abstract MultiMap headers(); - @Trace(async = true) - public HttpClientRequest response(Handler> handler) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response " + handler); - if (AgentBridge.getAgent().getTransaction(false) != null) { - this.token = NewRelic.getAgent().getTransaction().getToken(); - System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response"); - segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); - segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); - } - - return Weaver.callOriginal(); - } +// @Trace(async = true) +// public HttpClientRequest response(Handler> handler) { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response " + handler); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// this.token = NewRelic.getAgent().getTransaction().getToken(); +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// } +// +// return Weaver.callOriginal(); +// } @Trace(async = true) void handleResponse(Promise promise, HttpClientResponse resp, long timeoutMs) { AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse()"); - System.out.println("----------- linkAndExpire " + this.token.link() + " " + this.token); if (segment != null) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse() segment != null"); - System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); - Token segmentToken = segment.getTransaction().getToken(); - System.out.println("segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segmentToken); - reportExternal(resp, segment); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse() segment != null && this.token != null"); + final Token segmentToken = segment.getTransaction().getToken(); + reportExternal(resp, segment); segment.end(); - System.out.println("2222222222222 link " + segmentToken.link()); - System.out.println("33333 expire " + segmentToken.expire()); - } - this.token.expire(); - this.token = null; - Transaction t1 = AgentBridge.getAgent().getTransaction(); - Transaction t2 = NewRelic.getAgent().getTransaction(); + segmentToken.linkAndExpire(); + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } Weaver.callOriginal(); } @@ -91,6 +81,8 @@ void handleException(Throwable t) { final Token token = segment.getTransaction().getToken(); segment.end(); token.linkAndExpire(); + + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); } Weaver.callOriginal(); } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java index c0a9af31f7..f5ea66c890 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java @@ -25,50 +25,38 @@ @Weave(originalName = "io.vertx.core.http.impl.HttpClientRequestImpl") public abstract class HttpClientRequestImpl_Instrumentation extends HttpClientRequestBase_Instrumentation { -// public Future end(Buffer chunk) { -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk"); -// if (AgentBridge.getAgent().getTransaction(false) != null) { -// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk 2"); -// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); -// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); -// } -// return Weaver.callOriginal(); -// } -// -// public void end(Buffer chunk, Handler> handler) { -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler"); -// if (AgentBridge.getAgent().getTransaction(false) != null) { -// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler 2"); -// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); -// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); -// } -// Weaver.callOriginal(); -// } -// -// public Future end() { -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end()"); -// if (AgentBridge.getAgent().getTransaction(false) != null) { -// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end() 2"); -// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); -// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); -// System.out.println("end() Segment -- " + segment); -// } -// return Weaver.callOriginal(); -// } -// -// public void end(Handler> handler) { -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler)"); -// if (AgentBridge.getAgent().getTransaction(false) != null) { -// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler) 2"); -// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); -// System.out.println("!!!!segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segment.getTransaction().getToken()); -// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); -// System.out.println("end(Handler> Segment -- " + segment); -// } -// Weaver.callOriginal(); -// } + public Future end(Buffer chunk) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk"); + if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk 2"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + return Weaver.callOriginal(); + } + + public void end(Buffer chunk, Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler"); + if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler 2"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + Weaver.callOriginal(); + } + + public void end(Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler)"); + if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler) 2"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + System.out.println("!!!!segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segment.getTransaction().getToken()); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + System.out.println("end(Handler> Segment -- " + segment); + } + Weaver.callOriginal(); + } } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java index c5630d9a81..3570a9a149 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java @@ -6,6 +6,8 @@ */ package io.vertx.core.impl; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Transaction; import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -17,8 +19,10 @@ @Weave(originalName = "io.vertx.core.impl.AbstractContext") abstract class AbstractContext_Instrumentation { private static void setResultHandler(ContextInternal ctx, Future fut, Handler> resultHandler) { - System.out.println("setResulthandler ----"); - resultHandler = new AsyncHandlerWrapper<>(resultHandler, NewRelic.getAgent().getTransaction().getToken()); + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (txn != null) { + resultHandler = new AsyncHandlerWrapper<>(resultHandler, NewRelic.getAgent().getTransaction().getToken()); + } Weaver.callOriginal(); } } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java index 500a580c3d..32f0cff9ab 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java @@ -7,6 +7,8 @@ package io.vertx.core.impl; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Transaction; import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -18,8 +20,10 @@ @Weave(originalName = "io.vertx.core.impl.ContextImpl") abstract class ContextImpl_Instrumentation { static Future executeBlocking(ContextInternal context, Handler> blockingCodeHandler, WorkerPool workerPool, TaskQueue queue) { - System.out.println("executeBlocking ----"); - blockingCodeHandler = new PromiseHandlerWrapper(blockingCodeHandler, NewRelic.getAgent().getTransaction().getToken()); + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (txn != null) { + blockingCodeHandler = new PromiseHandlerWrapper(blockingCodeHandler, NewRelic.getAgent().getTransaction().getToken()); + } return Weaver.callOriginal(); } } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java index 251b351b99..c143d6ca2c 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java @@ -8,19 +8,13 @@ package io.vertx.core.impl.future; 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.vertx.instrumentation.VertxCoreUtil; -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Handler; @Weave(originalName = "io.vertx.core.impl.future.FutureImpl") abstract class FutureImpl_Instrumentation { - @NewField - private Handler handler = null; private Listener listener; public abstract boolean isComplete(); @@ -32,53 +26,18 @@ public void addListener(Listener listener) { } else { VertxCoreUtil.storeToken(listener); } -// if (!isComplete()) { -// System.out.println("addListener " + " " + this.listener + " " + listener + " " + System.identityHashCode(listener) + " " + System.currentTimeMillis()); -// assignAndStoreHandlerInstance(listener); -// } Weaver.callOriginal(); } - @Trace(async = true, excludeFromTransactionTrace = true) - public Future onSuccess(Handler handler) { - System.out.println("success"); - //assignAndStoreHandlerInstance(handler); - return Weaver.callOriginal(); - } - - @Trace(async = true, excludeFromTransactionTrace = true) - public Future onComplete(Handler handler) { - System.out.println("complete"); - //assignAndStoreHandlerInstance(handler); - return Weaver.callOriginal(); - } - - @Trace(async = true, excludeFromTransactionTrace = true) - public Future onFailure(Handler handler) { - //assignAndStoreHandlerInstance(handler); - return Weaver.callOriginal(); - } - @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryComplete(Object result) { - System.out.println("trycomplete " + " " + listener + " " + System.identityHashCode(this) + " " + System.currentTimeMillis()); VertxCoreUtil.linkAndExpireToken(this.listener); return Weaver.callOriginal(); } @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryFail(Throwable cause) { - System.out.println("tryFail"); VertxCoreUtil.linkAndExpireToken(this.listener); return Weaver.callOriginal(); } - - private void assignAndStoreHandlerInstance(Listener listener) { - VertxCoreUtil.storeToken(listener); - } - -// private void assignAndStoreHandlerInstance(Handler listener) { -// VertxCoreUtil.storeToken(listener); -// } - } diff --git a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java index 2aaa25e9d1..6b87f6b37e 100644 --- a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java +++ b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java @@ -19,11 +19,8 @@ import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.Trace; import io.vertx.core.Future; -import io.vertx.core.Handler; import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; import io.vertx.core.http.HttpMethod; @@ -78,34 +75,30 @@ public static void afterClass() { } @Test - public void testGet() throws InterruptedException { - getCall(); + public void testGet_withCallbacks() throws InterruptedException { + getCall_withCallbacks(); // Wait for transaction to finish - System.out.println("testGet " + NewRelic.getAgent().getTransaction()); InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbacks", "localhost"); } @Trace(dispatcher = true) - public void getCall() throws InterruptedException { - System.out.println("dispatcher -- " + NewRelic.getAgent().getTransaction()); + public void getCall_withCallbacks() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - //httpClient.close(); + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { if (reqAsyncResult.succeeded()) { //Request object successfully created HttpClientRequest request = reqAsyncResult.result(); - request.send(respAsyncResult -> { //Sending the request, waiting for response in ar2 + request.send(respAsyncResult -> { //Sending the request if (respAsyncResult.succeeded()) { - System.out.println("respAsyncResult succeed"); + NewRelic.addCustomParameter("responseHandler", "true"); HttpClientResponse response = respAsyncResult.result(); - int statusCode = response.statusCode(); latch.countDown(); - System.out.println("response: " + response + " " + statusCode); - response.body(respBufferAsyncResult -> { //Retriev response + response.body(respBufferAsyncResult -> { //Retrieve response if (respBufferAsyncResult.succeeded()) { - Buffer body = respBufferAsyncResult.result(); - // Handle body entirely + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body } else { // Handle server error, for example, connection closed } @@ -119,23 +112,34 @@ public void getCall() throws InterruptedException { } }); latch.await(); - - System.out.println("testGet " + NewRelic.getAgent().getTransaction()); } @Test - public void testGetNow() throws InterruptedException { - getNowCall(); + public void testGet_withCallbackAndFutures() throws InterruptedException { + getCall_withCallbackAndFutures(); // Wait for transaction to finish - InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getNowCall", "localhost"); + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbackAndFutures", "localhost"); } @Trace(dispatcher = true) - private void getNowCall() throws InterruptedException { + public void getCall_withCallbackAndFutures() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - //httpClient.getNow(port, "localhost", "/", requestHandler(latch)); + + httpClient.request(HttpMethod.GET, port, "localhost", "/", ar -> { + if (ar.succeeded()) { + HttpClientRequest request = ar.result(); + request.send("foo") + .onSuccess(response -> { + NewRelic.addCustomParameter("responseHandler", "true"); + NewRelic.addCustomParameter("bodyHandler", "true"); + latch.countDown(); + }).onFailure(err -> { + // + }); + } + }); latch.await(); } @@ -151,60 +155,25 @@ public void testPost() throws InterruptedException { private void postCall() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - //httpClient.post(port, "localhost", "/", requestHandler(latch)).end(); - latch.await(); - } - - @Test - public void testEndMethods() throws InterruptedException { - endMethods(); - assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); - final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( - "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/endMethods").iterator().next(); - assertEquals(4, externalRequest.getCount()); - assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); - assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/allOther")); - assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/all")); - } - - @Trace(dispatcher = true) - public void endMethods() throws InterruptedException { - HttpClient httpClient = vertx.createHttpClient(); - CountDownLatch latch = new CountDownLatch(4); - - Buffer bufferChunk = Buffer.buffer("buffer chunk!"); - String stringChunk = "string chunk!"; - String encoding = "UTF-8"; - - // tests the various overloaded versions of the end method - //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); - //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(bufferChunk); - //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk, encoding); - //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk); - latch.await(); - } - - @Test - public void testMethods() throws InterruptedException { - requestMethods(); - assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); - final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( - "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/requestMethods").iterator().next(); - assertEquals(5, externalRequest.getCount()); - assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); - assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/allOther")); - assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/all")); - } - @Trace(dispatcher = true) - public void requestMethods() throws InterruptedException { - HttpClient httpClient = vertx.createHttpClient(); - CountDownLatch latch = new CountDownLatch(5); - //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); - //httpClient.request(HttpMethod.POST, port, "localhost", "/hi", requestHandler(latch)).end(); - //httpClient.request(HttpMethod.PUT, port, "localhost", "/hi", requestHandler(latch)).end(); - //httpClient.request(HttpMethod.HEAD, port, "localhost", "/hi", requestHandler(latch)).end(); - //httpClient.request(HttpMethod.DELETE, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.POST, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); latch.await(); } @@ -218,13 +187,27 @@ public void testRedirect() throws InterruptedException { @Trace(dispatcher = true) public void redirect() throws InterruptedException { - HttpClient httpClient = vertx.createHttpClient(); CountDownLatch latch = new CountDownLatch(1); - //httpClient.get(port, "localhost", "/redirect") - // .putHeader("statusCode", "301") - // .setFollowRedirects(true) - // .handler(requestHandler(latch)) - // .end(); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.putHeader("statusCode", "301"); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); latch.await(); } @@ -278,10 +261,25 @@ public void cat(HttpTestServer httpServer) throws Exception { try { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - //httpClient.get(httpServer.getEndPoint().getPort(), httpServer.getEndPoint().getHost(), "/") - //.putHeader(HttpTestServer.DO_CAT, "true") - // .handler(requestHandler(latch)) - // .end(); + httpClient.request(HttpMethod.GET, httpServer.getEndPoint().getPort(),httpServer.getEndPoint().getHost(), "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.putHeader(HttpTestServer.DO_CAT, "true"); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); latch.await(); } finally { httpServer.shutdown(); @@ -289,17 +287,53 @@ public void cat(HttpTestServer httpServer) throws Exception { } @Test - public void testUnknownHost() throws Exception { - unknownHost(); + public void testUnknownHost_withCallbacks() throws Exception { + unknownHost_withCallbacks(); + assertUnknownHostExternal(); + } + + @Trace(dispatcher = true) + private void unknownHost_withCallbacks() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/", ar -> { + if (ar.failed()) { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + } + }); + latch.await(); + } + + @Test + public void testUnknownHost_withReturnedFuture() throws Exception { + unknownHost_withReturnedFuture(); + assertUnknownHostExternal(); + } + + @Trace(dispatcher = true) + private void unknownHost_withReturnedFuture() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + + Future r = httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/"); + r.onFailure(err -> { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + }); + latch.await(); + } + + private void assertUnknownHostExternal() { Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(250)); final String txn = introspector.getTransactionNames().iterator().next(); assertNotNull("Transaction not found", txn); - assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/end")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/handleResponse")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/handleResponse")); // Unknown hosts generate no external rollups assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/allOther")); @@ -310,47 +344,19 @@ public void testUnknownHost() throws Exception { assertTrue(event.getAttributes().containsKey("exceptionHandler")); } - @Trace(dispatcher = true) - private void unknownHost() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - HttpClient httpClient = vertx.createHttpClient(); - //httpClient.get(port, "notARealHostDuderina.com", "/") - // .exceptionHandler(exceptionHandler(latch)) - // .handler(requestHandler(null)) - // .end(); - latch.await(); - } - - private Handler exceptionHandler(CountDownLatch latch) { - return error -> { - NewRelic.addCustomParameter("exceptionHandler", "true"); - latch.countDown(); - }; - } - - private Handler requestHandler(CountDownLatch latch) { - return response -> { - NewRelic.addCustomParameter("responseHandler", "true"); - response.bodyHandler(body -> { - NewRelic.addCustomParameter("bodyHandler", "true"); - latch.countDown(); - }); - }; - } - - public void assertExternal(String transactionName, String host) { + private void assertExternal(String transactionName, String host) { Introspector introspector = InstrumentationTestRunner.getIntrospector(); Collection externalRequests = introspector.getExternalRequests(transactionName); ExternalRequest request = externalRequests.iterator().next(); assertEquals(host, request.getHostname()); assertEquals("Vertx-Client", request.getLibrary()); - assertEquals("end", request.getOperation()); + assertEquals("handleResponse", request.getOperation()); Collection events = introspector.getTransactionEvents(transactionName); TransactionEvent event = events.iterator().next(); assertTrue(event.getAttributes().containsKey("responseHandler")); - assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/end")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/handleResponse")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/handleResponse")); Collection transactionEvents = introspector.getTransactionEvents(transactionName); assertEquals(1, transactionEvents.size()); From bce201e76418e4ddb533cc9c7ba67a20e335deba Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Fri, 3 Nov 2023 07:31:54 -0400 Subject: [PATCH 03/16] v4.3.2 module commit --- instrumentation/vertx-core-4.3.2/build.gradle | 2 +- .../instrumentation/AsyncHandlerWrapper.java | 35 +++ .../HttpClientRequestPromiseWrapper.java | 158 ++++++++++ .../vertx/instrumentation/InboundWrapper.java | 2 +- .../instrumentation/OutboundWrapper.java | 2 +- .../PromiseHandlerWrapper.java | 36 +++ .../vertx/instrumentation/VertxCoreUtil.java | 19 +- .../impl/HttpClientImpl_Instrumentation.java | 22 ++ ...HttpClientRequestBase_Instrumentation.java | 37 ++- ...HttpClientRequestImpl_Instrumentation.java | 32 +- .../impl/ContextBase_Instrumentation.java | 47 +-- .../FutureImpl_Instrumentation.java | 23 +- .../instrumentation/VertxBlockingTest.java | 5 +- .../nr/vertx/instrumentation/VertxClient.java | 275 ++++++++++-------- .../nr/vertx/instrumentation/VertxFuture.java | 163 ++++++++--- .../java/com/newrelic/agent/TokenImpl.java | 1 - .../java/com/newrelic/agent/Transaction.java | 1 - 17 files changed, 631 insertions(+), 229 deletions(-) create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java rename instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/{ => future}/FutureImpl_Instrumentation.java (57%) diff --git a/instrumentation/vertx-core-4.3.2/build.gradle b/instrumentation/vertx-core-4.3.2/build.gradle index d3bd7ec4a5..b520cba843 100644 --- a/instrumentation/vertx-core-4.3.2/build.gradle +++ b/instrumentation/vertx-core-4.3.2/build.gradle @@ -9,7 +9,7 @@ jar { dependencies { implementation(project(":agent-bridge")) implementation("io.vertx:vertx-core:4.3.2") - testImplementation("io.vertx:vertx-core:4.0.0") + testImplementation("io.vertx:vertx-core:4.3.2") } verifyInstrumentation { diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java new file mode 100644 index 0000000000..00f88bfb4d --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java @@ -0,0 +1,35 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; + +public class AsyncHandlerWrapper implements Handler> { + private Handler> original; + + private Token token; + + public AsyncHandlerWrapper(Handler> original, Token token) { + this.original = original; + this.token = token; + } + + @Override + @Trace(async = true, excludeFromTransactionTrace = true) + public void handle(AsyncResult event) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + this.original.handle(event); + this.original = null; + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java new file mode 100644 index 0000000000..4c577f7fe0 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java @@ -0,0 +1,158 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.Transaction; +import io.netty.util.concurrent.Future; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.impl.ContextInternal; +import io.vertx.core.impl.future.Listener; +import io.vertx.core.impl.future.PromiseInternal; + +import java.util.function.Function; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +public class HttpClientRequestPromiseWrapper implements PromiseInternal { + + private final PromiseInternal original; + + private Token token; + + public HttpClientRequestPromiseWrapper(PromiseInternal original, Token token) { + this.original = original; + this.token = token; + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public void complete(HttpClientRequest req) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + original.complete(req); + } + + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryFail(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (t.toString().contains("UnknownHostException") && txn != null) { + Segment segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + VertxCoreUtil.reportUnknownHost(segment); + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } + + return original.tryFail(t); + } + + @Override + public boolean tryComplete(HttpClientRequest result) { + return original.tryComplete(result); + } + + @Override + public io.vertx.core.Future future() { + return original.future(); + } + + public ContextInternal context() { + return original.context(); + } + + @Override + public void addListener(Listener listener) { + original.addListener(listener); + } + + @Override + public void operationComplete(Future future) throws Exception { + original.operationComplete(future); + } + + @Override + public boolean isComplete() { + return original.isComplete(); + } + + @Override + public io.vertx.core.Future onComplete(Handler> handler) { + return original.onComplete(handler); + } + + @Override + public HttpClientRequest result() { + return original.result(); + } + + @Override + public Throwable cause() { + return original.cause(); + } + + @Override + public boolean succeeded() { + return original.succeeded(); + } + + @Override + public boolean failed() { + return original.failed(); + } + + @Override + public io.vertx.core.Future compose(Function> successMapper, + Function> failureMapper) { + return original.compose(successMapper, failureMapper); + } + + @Override + public io.vertx.core.Future transform(Function, io.vertx.core.Future> mapper) { + return original.transform(mapper); + } + + @Override + public io.vertx.core.Future eventually(Function> mapper) { + return original.eventually(mapper); + } + + @Override + public io.vertx.core.Future map(Function mapper) { + return original.map(mapper); + } + + @Override + public io.vertx.core.Future map(V value) { + return original.map(value); + } + + @Override + public io.vertx.core.Future otherwise(Function mapper) { + return original.otherwise(mapper); + } + + @Override + public io.vertx.core.Future otherwise(HttpClientRequest value) { + return original.otherwise(value); + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java index e7289a87b0..41531affba 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java index 0bc00583ca..7f4765a262 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java new file mode 100644 index 0000000000..0749efd318 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java @@ -0,0 +1,36 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Handler; +import io.vertx.core.Promise; + +public class PromiseHandlerWrapper implements Handler> { + + private Handler> original; + + private Token token; + + public PromiseHandlerWrapper(Handler> original, Token token) { + this.original = original; + this.token = token; + } + + @Override + @Trace(async = true, excludeFromTransactionTrace = true) + public void handle(Promise event) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + this.original.handle(event); + this.original = null; + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java index b48dd349ae..f85fdb7ebd 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ @@ -16,6 +16,7 @@ import com.newrelic.api.agent.weaver.Weaver; import io.vertx.core.Handler; import io.vertx.core.http.impl.HttpClientResponseImpl; +import io.vertx.core.impl.future.Listener; import java.net.URI; import java.net.URISyntaxException; @@ -29,19 +30,19 @@ private VertxCoreUtil() { private static final Map tokenMap = AgentBridge.collectionFactory.createConcurrentWeakKeyedMap(); public static final String VERTX_CLIENT = "Vertx-Client"; - public static final String END = "end"; + public static final String END = "handleResponse"; - private static URI UNKNOWN_HOST_URI = URI.create("http://UnknownHost/"); + private static final URI UNKNOWN_HOST_URI = URI.create("http://UnknownHost/"); - public static void storeToken(Handler handler) { - if (handler != null && AgentBridge.getAgent().getTransaction(false) != null) { - tokenMap.put(handler, NewRelic.getAgent().getTransaction().getToken()); + public static void storeToken(Listener listener) { + if (listener != null && AgentBridge.getAgent().getTransaction(false) != null) { + tokenMap.put(listener, NewRelic.getAgent().getTransaction().getToken()); } } - public static void linkAndExpireToken(Handler handler) { - if (handler != null) { - final Token token = tokenMap.remove(handler); + public static void linkAndExpireToken(Listener listener) { + if (listener != null) { + final Token token = tokenMap.remove(listener); if (token != null) { token.linkAndExpire(); } diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java new file mode 100644 index 0000000000..efed450c92 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java @@ -0,0 +1,22 @@ +package io.vertx.core.http.impl; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.HttpClientRequestPromiseWrapper; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.RequestOptions; +import io.vertx.core.impl.future.PromiseInternal; +import io.vertx.core.net.SocketAddress; + +@Weave(type = MatchType.BaseClass, originalName = "io.vertx.core.http.impl.HttpClientImpl") +public class HttpClientImpl_Instrumentation { + + private void doRequest(RequestOptions request, PromiseInternal promise) { + promise = new HttpClientRequestPromiseWrapper(promise, NewRelic.getAgent().getTransaction().getToken()); + Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java index 3cb00268f0..76e3c8225e 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -1,25 +1,29 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ package io.vertx.core.http.impl; +import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.Segment; 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.vertx.instrumentation.VertxCoreUtil; import io.vertx.core.MultiMap; +import io.vertx.core.Promise; import io.vertx.core.http.HttpClientResponse; import java.net.UnknownHostException; +import java.util.logging.Level; -@Weave(originalName = "io.vertx.core.http.impl.HttpClientRequestBase") +@Weave(type = MatchType.BaseClass, originalName = "io.vertx.core.http.impl.HttpClientRequestBase") public abstract class HttpClientRequestBase_Instrumentation { @NewField @@ -27,20 +31,39 @@ public abstract class HttpClientRequestBase_Instrumentation { public abstract MultiMap headers(); +// @Trace(async = true) +// public HttpClientRequest response(Handler> handler) { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response " + handler); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// this.token = NewRelic.getAgent().getTransaction().getToken(); +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// } +// +// return Weaver.callOriginal(); +// } + @Trace(async = true) - void handleResponse(HttpClientResponse resp) { + void handleResponse(Promise promise, HttpClientResponse resp, long timeoutMs) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse()"); if (segment != null) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse() segment != null && this.token != null"); + + final Token segmentToken = segment.getTransaction().getToken(); reportExternal(resp, segment); - final Token token = segment.getTransaction().getToken(); segment.end(); - token.linkAndExpire(); - } + segmentToken.linkAndExpire(); + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } Weaver.callOriginal(); } @Trace(async = true) void handleException(Throwable t) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleException()"); if (segment != null) { if (t instanceof UnknownHostException) { VertxCoreUtil.reportUnknownHost(segment); @@ -48,6 +71,8 @@ void handleException(Throwable t) { final Token token = segment.getTransaction().getToken(); segment.end(); token.linkAndExpire(); + + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); } Weaver.callOriginal(); } diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java index 54b6612f56..f5ea66c890 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ @@ -17,6 +17,8 @@ import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; +import java.util.logging.Level; + import static com.nr.vertx.instrumentation.VertxCoreUtil.END; import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; @@ -24,41 +26,37 @@ public abstract class HttpClientRequestImpl_Instrumentation extends HttpClientRequestBase_Instrumentation { public Future end(Buffer chunk) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk"); if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk 2"); segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); } - Weaver.callOriginal(); - - //hack - return null; + return Weaver.callOriginal(); } public void end(Buffer chunk, Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler"); if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler 2"); segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); } Weaver.callOriginal(); } - public Future end() { - if (AgentBridge.getAgent().getTransaction(false) != null) { - segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); - segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); - } - Weaver.callOriginal(); - - //hack - return null; - } - public void end(Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler)"); if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler) 2"); segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + System.out.println("!!!!segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segment.getTransaction().getToken()); segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + System.out.println("end(Handler> Segment -- " + segment); } Weaver.callOriginal(); } - } diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java index f896ac0976..606b08e9eb 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java @@ -1,36 +1,39 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ package io.vertx.core.impl; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Transaction; +import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.AsyncHandlerWrapper; +import com.nr.vertx.instrumentation.PromiseHandlerWrapper; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.Promise; @Weave(originalName = "io.vertx.core.impl.ContextBase") public abstract class ContextBase_Instrumentation { + static Future executeBlocking(ContextInternal context, Handler> blockingCodeHandler, WorkerPool workerPool, TaskQueue queue) { + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (txn != null) { + blockingCodeHandler = new PromiseHandlerWrapper(blockingCodeHandler, NewRelic.getAgent().getTransaction().getToken()); + } + return Weaver.callOriginal(); + } - //static Future executeBlocking(ContextInternal context, Handler> blockingCodeHandler, WorkerPool workerPool, TaskQueue queue) - -// void executeBlocking(Handler> blockingCodeHandler, Handler resultHandler, -// Executor exec, TaskQueue queue, PoolMetrics metrics) { -// VertxCoreUtil.storeToken(blockingCodeHandler); -// VertxCoreUtil.storeToken(resultHandler); -// Weaver.callOriginal(); -// } -// -// @Trace(async = true) -// private void lambda$executeBlocking$2(PoolMetrics metricsHandler, Object object, Handler handler, Handler resultHandler) { -// VertxCoreUtil.linkAndExpireToken(handler); -// Weaver.callOriginal(); -// } -// -// @Trace(async = true) -// private static void lambda$null$0(Handler handler, AsyncResult result, Void v) { -// VertxCoreUtil.linkAndExpireToken(handler); -// Weaver.callOriginal(); -// } - + static void setResultHandler(ContextInternal ctx, Future fut, Handler> resultHandler) { + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (txn != null) { + resultHandler = new AsyncHandlerWrapper<>(resultHandler, NewRelic.getAgent().getTransaction().getToken()); + } + Weaver.callOriginal(); + } } diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java similarity index 57% rename from instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java rename to instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java index 9266fcee33..c143d6ca2c 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java @@ -1,48 +1,43 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ -package io.vertx.core.impl; +package io.vertx.core.impl.future; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.vertx.instrumentation.VertxCoreUtil; -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Handler; @Weave(originalName = "io.vertx.core.impl.future.FutureImpl") abstract class FutureImpl_Instrumentation { - //private Handler handler = Weaver.callOriginal(); + private Listener listener; - //to use concrete method in instrumented class public abstract boolean isComplete(); - @Trace(async = true, excludeFromTransactionTrace = true) - public Future onComplete(Handler handler) { + public void addListener(Listener listener) { if (isComplete()) { - VertxCoreUtil.linkAndExpireToken(handler); + VertxCoreUtil.linkAndExpireToken(listener); } else { - VertxCoreUtil.storeToken(handler); + VertxCoreUtil.storeToken(listener); } - return Weaver.callOriginal(); + Weaver.callOriginal(); } @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryComplete(Object result) { - //VertxCoreUtil.linkAndExpireToken(handler); + VertxCoreUtil.linkAndExpireToken(this.listener); return Weaver.callOriginal(); } @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryFail(Throwable cause) { - //VertxCoreUtil.linkAndExpireToken(handler); + VertxCoreUtil.linkAndExpireToken(this.listener); return Weaver.callOriginal(); } } diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java index 622d18e349..0fa7ac126a 100644 --- a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ @@ -28,7 +28,7 @@ public class VertxBlockingTest { @Test - public void testBlocking() throws InterruptedException { + public void executeBlocking_withPromiseAndResult() throws InterruptedException { Vertx vertx = Vertx.vertx(); try { executeBlocking(vertx); @@ -56,5 +56,4 @@ void executeBlocking(Vertx vertx) throws InterruptedException { }); countDownLatch.await(); } - } diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java index 9e48e5905c..6b87f6b37e 100644 --- a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java @@ -18,9 +18,8 @@ import com.newrelic.agent.introspec.internal.HttpServerLocator; import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.Trace; -import io.vertx.core.Handler; +import io.vertx.core.Future; import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; @@ -44,20 +43,22 @@ @InstrumentationTestConfig(includePrefixes = { "io.vertx" }) public class VertxClient { -/* private static int port; - private static HttpServer server; + private static Future server; private static Vertx vertx; @BeforeClass public static void beforeClass() { port = getAvailablePort(); vertx = Vertx.vertx(); + server = vertx.createHttpServer().requestHandler(request -> { final String statusCode = request.getHeader("statusCode"); if (statusCode == null) { + System.out.println("statusCode is null"); request.response().end("response"); } else { + System.out.println("statusCode is NOT null -- " + statusCode); if (request.absoluteURI().equals("/redirect")) { request.headers().clear(); request.response().putHeader("Location", "http://localhost:" + port + "/other"); @@ -69,40 +70,76 @@ public static void beforeClass() { @AfterClass public static void afterClass() { - server.close(); + server.result().close(); vertx.close(); } @Test - public void testGet() throws InterruptedException { - getCall(); + public void testGet_withCallbacks() throws InterruptedException { + getCall_withCallbacks(); // Wait for transaction to finish - InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall", "localhost"); + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbacks", "localhost"); } @Trace(dispatcher = true) - public void getCall() throws InterruptedException { + public void getCall_withCallbacks() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - HttpClientRequest request = httpClient.options(port, "localhost", "/").handler(requestHandler(latch)); - request.end(); + + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } else { + // Handle server error, for example, connection closed + } + }); + } else { + // Handle server error, for example, connection closed + } + }); + } else { + // Connection error, for example, invalid server or invalid SSL certificate + } + }); latch.await(); } @Test - public void testGetNow() throws InterruptedException { - getNowCall(); + public void testGet_withCallbackAndFutures() throws InterruptedException { + getCall_withCallbackAndFutures(); // Wait for transaction to finish - InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getNowCall", "localhost"); + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbackAndFutures", "localhost"); } @Trace(dispatcher = true) - private void getNowCall() throws InterruptedException { + public void getCall_withCallbackAndFutures() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - httpClient.getNow(port, "localhost", "/", requestHandler(latch)); + + httpClient.request(HttpMethod.GET, port, "localhost", "/", ar -> { + if (ar.succeeded()) { + HttpClientRequest request = ar.result(); + request.send("foo") + .onSuccess(response -> { + NewRelic.addCustomParameter("responseHandler", "true"); + NewRelic.addCustomParameter("bodyHandler", "true"); + latch.countDown(); + }).onFailure(err -> { + // + }); + } + }); latch.await(); } @@ -118,60 +155,25 @@ public void testPost() throws InterruptedException { private void postCall() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - httpClient.post(port, "localhost", "/", requestHandler(latch)).end(); - latch.await(); - } - - @Test - public void testEndMethods() throws InterruptedException { - endMethods(); - assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); - final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( - "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/endMethods").iterator().next(); - assertEquals(4, externalRequest.getCount()); - assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); - assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/allOther")); - assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/all")); - } - - @Trace(dispatcher = true) - public void endMethods() throws InterruptedException { - HttpClient httpClient = vertx.createHttpClient(); - CountDownLatch latch = new CountDownLatch(4); - - Buffer bufferChunk = Buffer.buffer("buffer chunk!"); - String stringChunk = "string chunk!"; - String encoding = "UTF-8"; - // tests the various overloaded versions of the end method - httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); - httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(bufferChunk); - httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk, encoding); - httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk); - latch.await(); - } - - @Test - public void testMethods() throws InterruptedException { - requestMethods(); - assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); - final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( - "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/requestMethods").iterator().next(); - assertEquals(5, externalRequest.getCount()); - assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); - assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/allOther")); - assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/all")); - } - - @Trace(dispatcher = true) - public void requestMethods() throws InterruptedException { - HttpClient httpClient = vertx.createHttpClient(); - CountDownLatch latch = new CountDownLatch(5); - httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); - httpClient.request(HttpMethod.POST, port, "localhost", "/hi", requestHandler(latch)).end(); - httpClient.request(HttpMethod.PUT, port, "localhost", "/hi", requestHandler(latch)).end(); - httpClient.request(HttpMethod.HEAD, port, "localhost", "/hi", requestHandler(latch)).end(); - httpClient.request(HttpMethod.DELETE, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.POST, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); latch.await(); } @@ -185,13 +187,27 @@ public void testRedirect() throws InterruptedException { @Trace(dispatcher = true) public void redirect() throws InterruptedException { - HttpClient httpClient = vertx.createHttpClient(); CountDownLatch latch = new CountDownLatch(1); - httpClient.get(port, "localhost", "/redirect") - .putHeader("statusCode", "301") - .setFollowRedirects(true) - .handler(requestHandler(latch)) - .end(); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.putHeader("statusCode", "301"); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); latch.await(); } @@ -245,10 +261,25 @@ public void cat(HttpTestServer httpServer) throws Exception { try { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - httpClient.get(httpServer.getEndPoint().getPort(), httpServer.getEndPoint().getHost(), "/") - .putHeader(HttpTestServer.DO_CAT, "true") - .handler(requestHandler(latch)) - .end(); + httpClient.request(HttpMethod.GET, httpServer.getEndPoint().getPort(),httpServer.getEndPoint().getHost(), "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.putHeader(HttpTestServer.DO_CAT, "true"); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); latch.await(); } finally { httpServer.shutdown(); @@ -256,17 +287,53 @@ public void cat(HttpTestServer httpServer) throws Exception { } @Test - public void testUnknownHost() throws Exception { - unknownHost(); + public void testUnknownHost_withCallbacks() throws Exception { + unknownHost_withCallbacks(); + assertUnknownHostExternal(); + } + @Trace(dispatcher = true) + private void unknownHost_withCallbacks() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/", ar -> { + if (ar.failed()) { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + } + }); + latch.await(); + } + + @Test + public void testUnknownHost_withReturnedFuture() throws Exception { + unknownHost_withReturnedFuture(); + assertUnknownHostExternal(); + } + + @Trace(dispatcher = true) + private void unknownHost_withReturnedFuture() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + + Future r = httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/"); + r.onFailure(err -> { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + }); + + latch.await(); + } + + private void assertUnknownHostExternal() { Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(250)); final String txn = introspector.getTransactionNames().iterator().next(); assertNotNull("Transaction not found", txn); - assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/end")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/handleResponse")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/handleResponse")); // Unknown hosts generate no external rollups assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/allOther")); @@ -277,47 +344,19 @@ public void testUnknownHost() throws Exception { assertTrue(event.getAttributes().containsKey("exceptionHandler")); } - @Trace(dispatcher = true) - private void unknownHost() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - HttpClient httpClient = vertx.createHttpClient(); - httpClient.get(port, "notARealHostDuderina.com", "/") - .exceptionHandler(exceptionHandler(latch)) - .handler(requestHandler(null)) - .end(); - latch.await(); - } - - private Handler exceptionHandler(CountDownLatch latch) { - return error -> { - NewRelic.addCustomParameter("exceptionHandler", "true"); - latch.countDown(); - }; - } - - private Handler requestHandler(CountDownLatch latch) { - return response -> { - NewRelic.addCustomParameter("responseHandler", "true"); - response.bodyHandler(body -> { - NewRelic.addCustomParameter("bodyHandler", "true"); - latch.countDown(); - }); - }; - } - - public void assertExternal(String transactionName, String host) { + private void assertExternal(String transactionName, String host) { Introspector introspector = InstrumentationTestRunner.getIntrospector(); Collection externalRequests = introspector.getExternalRequests(transactionName); ExternalRequest request = externalRequests.iterator().next(); assertEquals(host, request.getHostname()); assertEquals("Vertx-Client", request.getLibrary()); - assertEquals("end", request.getOperation()); + assertEquals("handleResponse", request.getOperation()); Collection events = introspector.getTransactionEvents(transactionName); TransactionEvent event = events.iterator().next(); assertTrue(event.getAttributes().containsKey("responseHandler")); - assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/end")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/handleResponse")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/handleResponse")); Collection transactionEvents = introspector.getTransactionEvents(transactionName); assertEquals(1, transactionEvents.size()); @@ -343,6 +382,4 @@ private static int getAvailablePort() { } return port; } -*/ - } diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java index 831f9e01d1..f3676b8fd0 100644 --- a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java @@ -15,13 +15,13 @@ import com.newrelic.api.agent.Trace; import io.vertx.core.CompositeFuture; import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Map; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -32,74 +32,169 @@ public class VertxFuture { @Test public void testCompositeFuture() throws InterruptedException { - compositeFutures(); + compositeFuturesAllFuturesSucceed(); Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFutures"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFuturesAllFuturesSucceed"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); - assertTrue(attributes.containsKey("success")); + assertTrue(attributes.containsKey("compositeFuture")); } @Trace(dispatcher = true) - private void compositeFutures() throws InterruptedException { - final Future abc = Future.future(); - final Future def = Future.future(); - - final ExecutorService service = Executors.newSingleThreadExecutor(); - + private void compositeFuturesAllFuturesSucceed() throws InterruptedException { + Vertx vertx = Vertx.vertx(); CountDownLatch latch = new CountDownLatch(1); - CompositeFuture.all(abc, def).setHandler(result -> { - if (result.succeeded()) { - NewRelic.addCustomParameter("success", "yes"); + Promise promise1 = Promise.promise(); + Promise promise2 = Promise.promise(); + + CompositeFuture.all(promise1.future(), promise2.future()).onComplete((ar -> { + if (ar.succeeded()) { + NewRelic.addCustomParameter("compositeFuture", "yes"); } latch.countDown(); - }); + })); - service.submit(() -> { - abc.complete("abc"); - }); - - service.submit(() -> { - def.complete("def"); + vertx.setTimer(1, handler -> { + promise1.complete("promise1"); + promise2.complete("promise2"); }); latch.await(); } @Test - public void testFutureFail() throws InterruptedException { - failFuture(); + public void whenFutureFails_withThrowable_txnStillCompletes() throws InterruptedException { + failFutureWithThrowable(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithThrowable"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureFails_withString_txnStillCompletes() throws InterruptedException { + failFutureWithString(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithString"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureCompletes_txnStillCompletes() throws InterruptedException { + completeFutureSuccessfully(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfully"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureCompletes_withOnCompleteRegistered_txnStillCompletes() throws InterruptedException { + completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback(); Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFuture"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); } @Trace(dispatcher = true) - private void failFuture() throws InterruptedException { + private void failFutureWithString() throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(1); - Future future = Future.future(); - future.setHandler(ar -> { - if (ar.failed()) { - NewRelic.addCustomParameter("future", "failed"); - countDownLatch.countDown(); - } + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onFailure(ar -> { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.fail("oops"); + }); + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void failFutureWithThrowable() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onFailure(ar -> { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); }); - ExecutorService service = Executors.newSingleThreadExecutor(); - service.submit(() -> { - future.fail(new RuntimeException()); + vertx.setTimer(1, handler -> { + promise.fail(new IllegalArgumentException("foo")); }); countDownLatch.await(); } + @Trace(dispatcher = true) + private void completeFutureSuccessfully() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onSuccess(ar -> { + NewRelic.addCustomParameter("future", "success"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.complete("hooray"); + }); + + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onComplete(ar -> { + NewRelic.addCustomParameter("future", "onComplete"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.complete("hooray"); + }); + + countDownLatch.await(); + } } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java index 1fe9616a7e..7aabd7a552 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java @@ -58,7 +58,6 @@ public Tracer getInitiatingTracer() { @Override public boolean expire() { - System.out.println("expire --- " + this); if (active.compareAndSet(Boolean.TRUE, Boolean.FALSE)) { Transaction tx = getTransaction().getTransactionIfExists(); if (tx != null) { diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java b/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java index ff250212a2..1b6af54e83 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java @@ -2209,7 +2209,6 @@ public Token getToken() { getMetricAggregator().incrementCounter(AgentBridge.currentApiSource.get().getSupportabilityMetric( MetricNames.SUPPORTABILITY_API_TOKEN)); - System.out.println("getToken --- " + token); return token; } From 9cbe21b94b4d39186652596ac4191b02348e3da2 Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Fri, 3 Nov 2023 09:21:38 -0400 Subject: [PATCH 04/16] correct verifyInstr libraries for earlier vertx modules --- instrumentation/vertx-core-3.3.0/build.gradle | 2 +- instrumentation/vertx-core-3.3.3/build.gradle | 2 +- instrumentation/vertx-core-3.4.1/build.gradle | 2 +- instrumentation/vertx-core-3.6.0/build.gradle | 2 +- instrumentation/vertx-core-3.8.0/build.gradle | 2 +- instrumentation/vertx-core-3.9.0/build.gradle | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/instrumentation/vertx-core-3.3.0/build.gradle b/instrumentation/vertx-core-3.3.0/build.gradle index ce4a2e627e..98b6f7c13a 100644 --- a/instrumentation/vertx-core-3.3.0/build.gradle +++ b/instrumentation/vertx-core-3.3.0/build.gradle @@ -12,7 +12,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-web:[3.3.0.CR1,3.3.3)' + passesOnly 'io.vertx:vertx-core:[3.3.0.CR1,3.3.3)' } site { diff --git a/instrumentation/vertx-core-3.3.3/build.gradle b/instrumentation/vertx-core-3.3.3/build.gradle index 37a85ff04d..c557a288b6 100644 --- a/instrumentation/vertx-core-3.3.3/build.gradle +++ b/instrumentation/vertx-core-3.3.3/build.gradle @@ -10,7 +10,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-web:[3.3.3,3.4.0]' + passesOnly 'io.vertx:vertx-core:[3.3.3,3.4.0]' } test { diff --git a/instrumentation/vertx-core-3.4.1/build.gradle b/instrumentation/vertx-core-3.4.1/build.gradle index 6de5290d69..b2b2e80de6 100644 --- a/instrumentation/vertx-core-3.4.1/build.gradle +++ b/instrumentation/vertx-core-3.4.1/build.gradle @@ -13,7 +13,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-web:[3.4.1,3.6.0)' + passesOnly 'io.vertx:vertx-core:[3.4.1,3.6.0)' excludeRegex '.*CR[0-9]*' } diff --git a/instrumentation/vertx-core-3.6.0/build.gradle b/instrumentation/vertx-core-3.6.0/build.gradle index 8ec0f53cc8..4237e1bcfa 100644 --- a/instrumentation/vertx-core-3.6.0/build.gradle +++ b/instrumentation/vertx-core-3.6.0/build.gradle @@ -13,7 +13,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-web:[3.6.0,3.8.0)' + passesOnly 'io.vertx:vertx-core:[3.6.0,3.8.0)' excludeRegex '.*CR[0-9]*' } diff --git a/instrumentation/vertx-core-3.8.0/build.gradle b/instrumentation/vertx-core-3.8.0/build.gradle index 3cb6bad4ee..7d7883bfaf 100644 --- a/instrumentation/vertx-core-3.8.0/build.gradle +++ b/instrumentation/vertx-core-3.8.0/build.gradle @@ -13,7 +13,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-web:[3.8.0,3.9.0)' + passesOnly 'io.vertx:vertx-core:[3.8.0,3.9.0)' excludeRegex '.*CR[0-9]*' excludeRegex '.*-milestone[0-9]' } diff --git a/instrumentation/vertx-core-3.9.0/build.gradle b/instrumentation/vertx-core-3.9.0/build.gradle index 13f7603af5..671b75e47d 100644 --- a/instrumentation/vertx-core-3.9.0/build.gradle +++ b/instrumentation/vertx-core-3.9.0/build.gradle @@ -13,7 +13,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-web:[3.9.0,4.0.0.Beta1)' + passesOnly 'io.vertx:vertx-core:[3.9.0,4.0.0.Beta1)' excludeRegex '.*CR[0-9]*' excludeRegex '.*-milestone[0-9]' } From f8d2010253a735e6a752b99fc8bd923f6d291d42 Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Thu, 9 Nov 2023 11:34:28 -0500 Subject: [PATCH 05/16] v4.1.0 module --- instrumentation/vertx-core-4.0.0/build.gradle | 2 +- instrumentation/vertx-core-4.1.0/.gitignore | 42 ++ instrumentation/vertx-core-4.1.0/README.md | 42 ++ instrumentation/vertx-core-4.1.0/build.gradle | 25 ++ .../instrumentation/AsyncHandlerWrapper.java | 35 ++ .../HttpClientRequestPromiseWrapper.java | 158 +++++++ .../vertx/instrumentation/InboundWrapper.java | 32 ++ .../instrumentation/OutboundWrapper.java | 32 ++ .../PromiseHandlerWrapper.java | 36 ++ .../vertx/instrumentation/VertxCoreUtil.java | 73 ++++ .../impl/HttpClientImpl_Instrumentation.java | 18 + ...HttpClientRequestBase_Instrumentation.java | 99 +++++ ...HttpClientRequestImpl_Instrumentation.java | 62 +++ .../impl/AbstractContext_Instrumentation.java | 28 ++ .../impl/ContextImpl_Instrumentation.java | 29 ++ .../future/FutureImpl_Instrumentation.java | 43 ++ .../instrumentation/VertxBlockingTest.java | 59 +++ .../nr/vertx/instrumentation/VertxClient.java | 385 ++++++++++++++++++ .../nr/vertx/instrumentation/VertxFuture.java | 200 +++++++++ settings.gradle | 1 + 20 files changed, 1400 insertions(+), 1 deletion(-) create mode 100644 instrumentation/vertx-core-4.1.0/.gitignore create mode 100644 instrumentation/vertx-core-4.1.0/README.md create mode 100644 instrumentation/vertx-core-4.1.0/build.gradle create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java create mode 100644 instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java create mode 100644 instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java diff --git a/instrumentation/vertx-core-4.0.0/build.gradle b/instrumentation/vertx-core-4.0.0/build.gradle index e17b5f6b24..6dc20f91a9 100644 --- a/instrumentation/vertx-core-4.0.0/build.gradle +++ b/instrumentation/vertx-core-4.0.0/build.gradle @@ -13,7 +13,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-core:[4.0.0,4.3.2)' + passesOnly 'io.vertx:vertx-core:[4.0.0,4.1.0)' excludeRegex '.*CR[0-9]*' excludeRegex '.*-milestone[0-9]' excludeRegex '.*Beta[0-9]' diff --git a/instrumentation/vertx-core-4.1.0/.gitignore b/instrumentation/vertx-core-4.1.0/.gitignore new file mode 100644 index 0000000000..b63da4551b --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/instrumentation/vertx-core-4.1.0/README.md b/instrumentation/vertx-core-4.1.0/README.md new file mode 100644 index 0000000000..36cd1a1c4d --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/README.md @@ -0,0 +1,42 @@ +Vert.x 4.00 Core Instrumentation +================================ + +### HTTP Client + +The Vert.x core instrumentation module is mainly concerned with instrumenting the low level Vert.x HTTP Client and associated Future instances. + +The instrumentation module can instrument client instances created various ways. For example, via inline callbacks: +```text + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.POST, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + HttpClientResponse response = respAsyncResult.result(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + // Handle body + } + }); + } + }); + } + }); +``` + +Or clients created that utilize Futures for response handling: +```text + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port, "localhost", "/", ar -> { + if (ar.succeeded()) { + HttpClientRequest request = ar.result(); + request.send("foo") + .onSuccess(response -> { + //Success handler + }).onFailure(err -> { + //Failure handler + }); + } + }); +``` diff --git a/instrumentation/vertx-core-4.1.0/build.gradle b/instrumentation/vertx-core-4.1.0/build.gradle new file mode 100644 index 0000000000..45305a96c1 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/build.gradle @@ -0,0 +1,25 @@ +jar { + manifest { + attributes 'Implementation-Title': 'com.newrelic.instrumentation.vertx-core-4.1.0' + } +} + + + +dependencies { + implementation(project(":agent-bridge")) + implementation("io.vertx:vertx-core:4.1.0") + testImplementation("io.vertx:vertx-core:4.1.0") +} + +verifyInstrumentation { + passesOnly 'io.vertx:vertx-core:[4.1.0,4.3.2)' + excludeRegex '.*CR[0-9]*' + excludeRegex '.*-milestone[0-9]' + excludeRegex '.*Beta[0-9]' +} + +site { + title 'Vertx' + type 'Framework' +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java new file mode 100644 index 0000000000..00f88bfb4d --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java @@ -0,0 +1,35 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; + +public class AsyncHandlerWrapper implements Handler> { + private Handler> original; + + private Token token; + + public AsyncHandlerWrapper(Handler> original, Token token) { + this.original = original; + this.token = token; + } + + @Override + @Trace(async = true, excludeFromTransactionTrace = true) + public void handle(AsyncResult event) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + this.original.handle(event); + this.original = null; + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java new file mode 100644 index 0000000000..4c577f7fe0 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java @@ -0,0 +1,158 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.Transaction; +import io.netty.util.concurrent.Future; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.impl.ContextInternal; +import io.vertx.core.impl.future.Listener; +import io.vertx.core.impl.future.PromiseInternal; + +import java.util.function.Function; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +public class HttpClientRequestPromiseWrapper implements PromiseInternal { + + private final PromiseInternal original; + + private Token token; + + public HttpClientRequestPromiseWrapper(PromiseInternal original, Token token) { + this.original = original; + this.token = token; + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public void complete(HttpClientRequest req) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + original.complete(req); + } + + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryFail(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (t.toString().contains("UnknownHostException") && txn != null) { + Segment segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + VertxCoreUtil.reportUnknownHost(segment); + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } + + return original.tryFail(t); + } + + @Override + public boolean tryComplete(HttpClientRequest result) { + return original.tryComplete(result); + } + + @Override + public io.vertx.core.Future future() { + return original.future(); + } + + public ContextInternal context() { + return original.context(); + } + + @Override + public void addListener(Listener listener) { + original.addListener(listener); + } + + @Override + public void operationComplete(Future future) throws Exception { + original.operationComplete(future); + } + + @Override + public boolean isComplete() { + return original.isComplete(); + } + + @Override + public io.vertx.core.Future onComplete(Handler> handler) { + return original.onComplete(handler); + } + + @Override + public HttpClientRequest result() { + return original.result(); + } + + @Override + public Throwable cause() { + return original.cause(); + } + + @Override + public boolean succeeded() { + return original.succeeded(); + } + + @Override + public boolean failed() { + return original.failed(); + } + + @Override + public io.vertx.core.Future compose(Function> successMapper, + Function> failureMapper) { + return original.compose(successMapper, failureMapper); + } + + @Override + public io.vertx.core.Future transform(Function, io.vertx.core.Future> mapper) { + return original.transform(mapper); + } + + @Override + public io.vertx.core.Future eventually(Function> mapper) { + return original.eventually(mapper); + } + + @Override + public io.vertx.core.Future map(Function mapper) { + return original.map(mapper); + } + + @Override + public io.vertx.core.Future map(V value) { + return original.map(value); + } + + @Override + public io.vertx.core.Future otherwise(Function mapper) { + return original.otherwise(mapper); + } + + @Override + public io.vertx.core.Future otherwise(HttpClientRequest value) { + return original.otherwise(value); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java new file mode 100644 index 0000000000..41531affba --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.InboundHeaders; +import io.vertx.core.http.HttpClientResponse; + +public class InboundWrapper implements InboundHeaders { + + private final HttpClientResponse response; + + public InboundWrapper(HttpClientResponse response) { + this.response = response; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public String getHeader(String name) { + return response.getHeader(name); + } + +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java new file mode 100644 index 0000000000..7f4765a262 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.OutboundHeaders; +import io.vertx.core.MultiMap; + +public class OutboundWrapper implements OutboundHeaders { + + private final MultiMap headers; + + public OutboundWrapper(MultiMap headers) { + this.headers = headers; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public void setHeader(String name, String value) { + headers.add(name, value); + } + +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java new file mode 100644 index 0000000000..0749efd318 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java @@ -0,0 +1,36 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Handler; +import io.vertx.core.Promise; + +public class PromiseHandlerWrapper implements Handler> { + + private Handler> original; + + private Token token; + + public PromiseHandlerWrapper(Handler> original, Token token) { + this.original = original; + this.token = token; + } + + @Override + @Trace(async = true, excludeFromTransactionTrace = true) + public void handle(Promise event) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + this.original.handle(event); + this.original = null; + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java new file mode 100644 index 0000000000..cb98790edb --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java @@ -0,0 +1,73 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.GenericParameters; +import com.newrelic.api.agent.HttpParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Transaction; +import com.newrelic.api.agent.weaver.Weaver; +import io.vertx.core.Handler; +import io.vertx.core.http.impl.HttpClientResponseImpl; +import io.vertx.core.impl.future.Listener; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; + +public class VertxCoreUtil { + + private VertxCoreUtil() { + } + + private static final Map tokenMap = AgentBridge.collectionFactory.createConcurrentWeakKeyedMap(); + + public static final String VERTX_CLIENT = "Vertx-Client"; + public static final String END = "handleResponse"; + + private static final URI UNKNOWN_HOST_URI = URI.create("http://UnknownHost/"); + + public static void storeToken(Listener handler) { + if (handler != null && AgentBridge.getAgent().getTransaction(false) != null) { + tokenMap.put(handler, NewRelic.getAgent().getTransaction().getToken()); + } + } + + public static void linkAndExpireToken(Listener handler) { + if (handler != null) { + final Token token = tokenMap.remove(handler); + if (token != null) { + token.linkAndExpire(); + } + } + } + + public static void processResponse(Segment segment, HttpClientResponseImpl resp, String host, int port, + String scheme) { + try { + URI uri = new URI(scheme, null, host, port, null, null, null); + segment.reportAsExternal(HttpParameters.library(VERTX_CLIENT) + .uri(uri) + .procedure(END) + .inboundHeaders(new InboundWrapper(resp)) + .build()); + } catch (URISyntaxException e) { + AgentBridge.instrumentation.noticeInstrumentationError(e, Weaver.getImplementationTitle()); + } + } + + public static void reportUnknownHost(Segment segment) { + segment.reportAsExternal(GenericParameters.library(VERTX_CLIENT) + .uri(UNKNOWN_HOST_URI) + .procedure(END) + .build()); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java new file mode 100644 index 0000000000..9b7eff5baf --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java @@ -0,0 +1,18 @@ +package io.vertx.core.http.impl; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.HttpClientRequestPromiseWrapper; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.RequestOptions; +import io.vertx.core.impl.future.PromiseInternal; + +@Weave(type = MatchType.BaseClass, originalName = "io.vertx.core.http.impl.HttpClientImpl") +public class HttpClientImpl_Instrumentation { + private void doRequest(RequestOptions request, PromiseInternal requestPromise) { + requestPromise = new HttpClientRequestPromiseWrapper(requestPromise, NewRelic.getAgent().getTransaction().getToken()); + Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java new file mode 100644 index 0000000000..a59e0a2c74 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -0,0 +1,99 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.http.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.Transaction; +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.vertx.instrumentation.AsyncHandlerWrapper; +import com.nr.vertx.instrumentation.OutboundWrapper; +import com.nr.vertx.instrumentation.VertxCoreUtil; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.MultiMap; +import io.vertx.core.Promise; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; + +import java.net.UnknownHostException; +import java.util.logging.Level; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +@Weave(type = MatchType.BaseClass, originalName = "io.vertx.core.http.impl.HttpClientRequestBase") +public abstract class HttpClientRequestBase_Instrumentation { + + @NewField + public Segment segment; + + public abstract MultiMap headers(); + +// @Trace(async = true) +// public HttpClientRequest response(Handler> handler) { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response " + handler); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// this.token = NewRelic.getAgent().getTransaction().getToken(); +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// } +// +// return Weaver.callOriginal(); +// } + + @Trace(async = true) + void handleResponse(Promise promise, HttpClientResponse resp, long timeoutMs) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse()"); + if (segment != null) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse() segment != null && this.token != null"); + + final Token segmentToken = segment.getTransaction().getToken(); + reportExternal(resp, segment); + segment.end(); + segmentToken.linkAndExpire(); + + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } + Weaver.callOriginal(); + } + + @Trace(async = true) + void handleException(Throwable t) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleException()"); + if (segment != null) { + if (t instanceof UnknownHostException) { + VertxCoreUtil.reportUnknownHost(segment); + } + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } + Weaver.callOriginal(); + } + + private void reportExternal(HttpClientResponse response, Segment segment) { + if (response instanceof HttpClientResponseImpl) { + HttpClientResponseImpl resp = (HttpClientResponseImpl) response; + final String host = resp.request().getHost(); + final int port = resp.request().getPort(); + final String scheme = resp.request().ssl ? "https" : "http"; + VertxCoreUtil.processResponse(segment, resp, host, port, scheme); + } + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java new file mode 100644 index 0000000000..f5ea66c890 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java @@ -0,0 +1,62 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.http.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.OutboundWrapper; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; + +import java.util.logging.Level; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +@Weave(originalName = "io.vertx.core.http.impl.HttpClientRequestImpl") +public abstract class HttpClientRequestImpl_Instrumentation extends HttpClientRequestBase_Instrumentation { + + public Future end(Buffer chunk) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk"); + if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk 2"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + return Weaver.callOriginal(); + } + + public void end(Buffer chunk, Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler"); + if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler 2"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + Weaver.callOriginal(); + } + + public void end(Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler)"); + if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler) 2"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + System.out.println("!!!!segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segment.getTransaction().getToken()); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + System.out.println("end(Handler> Segment -- " + segment); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java new file mode 100644 index 0000000000..3570a9a149 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java @@ -0,0 +1,28 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package io.vertx.core.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Transaction; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.AsyncHandlerWrapper; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; + +@Weave(originalName = "io.vertx.core.impl.AbstractContext") +abstract class AbstractContext_Instrumentation { + private static void setResultHandler(ContextInternal ctx, Future fut, Handler> resultHandler) { + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (txn != null) { + resultHandler = new AsyncHandlerWrapper<>(resultHandler, NewRelic.getAgent().getTransaction().getToken()); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java new file mode 100644 index 0000000000..32f0cff9ab --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java @@ -0,0 +1,29 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Transaction; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.PromiseHandlerWrapper; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.Promise; + +@Weave(originalName = "io.vertx.core.impl.ContextImpl") +abstract class ContextImpl_Instrumentation { + static Future executeBlocking(ContextInternal context, Handler> blockingCodeHandler, WorkerPool workerPool, TaskQueue queue) { + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (txn != null) { + blockingCodeHandler = new PromiseHandlerWrapper(blockingCodeHandler, NewRelic.getAgent().getTransaction().getToken()); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java new file mode 100644 index 0000000000..c143d6ca2c --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java @@ -0,0 +1,43 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.impl.future; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.VertxCoreUtil; + +@Weave(originalName = "io.vertx.core.impl.future.FutureImpl") +abstract class FutureImpl_Instrumentation { + + private Listener listener; + + public abstract boolean isComplete(); + + @Trace(async = true, excludeFromTransactionTrace = true) + public void addListener(Listener listener) { + if (isComplete()) { + VertxCoreUtil.linkAndExpireToken(listener); + } else { + VertxCoreUtil.storeToken(listener); + } + Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryComplete(Object result) { + VertxCoreUtil.linkAndExpireToken(this.listener); + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryFail(Throwable cause) { + VertxCoreUtil.linkAndExpireToken(this.listener); + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java new file mode 100644 index 0000000000..0fa7ac126a --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java @@ -0,0 +1,59 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Vertx; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxBlockingTest { + + @Test + public void executeBlocking_withPromiseAndResult() throws InterruptedException { + Vertx vertx = Vertx.vertx(); + try { + executeBlocking(vertx); + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxBlockingTest/executeBlocking"; + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("InFuture")); + assertTrue(attributes.containsKey("InResponseHandler")); + } finally { + vertx.close(); + } + } + + @Trace(dispatcher = true) + void executeBlocking(Vertx vertx) throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + vertx.executeBlocking(future -> { + NewRelic.addCustomParameter("InFuture", "yes"); + future.complete(); + }, res -> { + NewRelic.addCustomParameter("InResponseHandler", "yes"); + countDownLatch.countDown(); + }); + countDownLatch.await(); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java new file mode 100644 index 0000000000..6b87f6b37e --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java @@ -0,0 +1,385 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.CatHelper; +import com.newrelic.agent.introspec.ExternalRequest; +import com.newrelic.agent.introspec.HttpTestServer; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.MetricsHelper; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.agent.introspec.internal.HttpServerLocator; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.Collection; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxClient { + + private static int port; + private static Future server; + private static Vertx vertx; + + @BeforeClass + public static void beforeClass() { + port = getAvailablePort(); + vertx = Vertx.vertx(); + + server = vertx.createHttpServer().requestHandler(request -> { + final String statusCode = request.getHeader("statusCode"); + if (statusCode == null) { + System.out.println("statusCode is null"); + request.response().end("response"); + } else { + System.out.println("statusCode is NOT null -- " + statusCode); + if (request.absoluteURI().equals("/redirect")) { + request.headers().clear(); + request.response().putHeader("Location", "http://localhost:" + port + "/other"); + } + request.response().setStatusCode(Integer.parseInt(statusCode)).end("response"); + } + }).listen(port); + } + + @AfterClass + public static void afterClass() { + server.result().close(); + vertx.close(); + } + + @Test + public void testGet_withCallbacks() throws InterruptedException { + getCall_withCallbacks(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbacks", "localhost"); + } + + @Trace(dispatcher = true) + public void getCall_withCallbacks() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } else { + // Handle server error, for example, connection closed + } + }); + } else { + // Handle server error, for example, connection closed + } + }); + } else { + // Connection error, for example, invalid server or invalid SSL certificate + } + }); + latch.await(); + } + + @Test + public void testGet_withCallbackAndFutures() throws InterruptedException { + getCall_withCallbackAndFutures(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbackAndFutures", "localhost"); + } + + @Trace(dispatcher = true) + public void getCall_withCallbackAndFutures() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + + httpClient.request(HttpMethod.GET, port, "localhost", "/", ar -> { + if (ar.succeeded()) { + HttpClientRequest request = ar.result(); + request.send("foo") + .onSuccess(response -> { + NewRelic.addCustomParameter("responseHandler", "true"); + NewRelic.addCustomParameter("bodyHandler", "true"); + latch.countDown(); + }).onFailure(err -> { + // + }); + } + }); + latch.await(); + } + + @Test + public void testPost() throws InterruptedException { + postCall(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/postCall", "localhost"); + } + + @Trace(dispatcher = true) + private void postCall() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + + httpClient.request(HttpMethod.POST, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); + latch.await(); + } + + @Test + public void testRedirect() throws InterruptedException { + redirect(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/redirect", "localhost"); + } + + @Trace(dispatcher = true) + public void redirect() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.putHeader("statusCode", "301"); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); + latch.await(); + } + + @Test + public void testCat() throws Exception { + try (HttpTestServer httpServer = HttpServerLocator.createAndStart()) { + cat(httpServer); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + String host = httpServer.getEndPoint().getHost(); + String txName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/cat"; + assertEquals(2, introspector.getFinishedTransactionCount(250)); + Collection names = introspector.getTransactionNames(); + assertEquals(2, names.size()); + assertTrue(names.contains(httpServer.getServerTransactionName())); + assertTrue(names.contains(txName)); + + // scoped metrics + assertEquals(1, MetricsHelper.getScopedMetricCount(txName, "ExternalTransaction/" + host + "/" + + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); + assertEquals(1, MetricsHelper.getScopedMetricCount(txName, + "Java/com.nr.vertx.instrumentation.VertxClient/cat")); + + // unscoped metrics + assertEquals(1, MetricsHelper.getUnscopedMetricCount("ExternalTransaction/" + host + "/" + + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/" + host + "/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); + + // events + Collection transactionEvents = introspector.getTransactionEvents(txName); + assertEquals(1, transactionEvents.size()); + TransactionEvent transactionEvent = transactionEvents.iterator().next(); + assertEquals(1, transactionEvent.getExternalCallCount()); + assertTrue(transactionEvent.getExternalDurationInSec() > 0); + + CatHelper.verifyOneSuccessfulCat(introspector, txName); + + // external request information + Collection externalRequests = introspector.getExternalRequests(txName); + assertEquals(1, externalRequests.size()); + ExternalRequest externalRequest = externalRequests.iterator().next(); + assertEquals(1, externalRequest.getCount()); + assertEquals(host, externalRequest.getHostname()); + } + } + + @Trace(dispatcher = true) + public void cat(HttpTestServer httpServer) throws Exception { + try { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, httpServer.getEndPoint().getPort(),httpServer.getEndPoint().getHost(), "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.putHeader(HttpTestServer.DO_CAT, "true"); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); + latch.await(); + } finally { + httpServer.shutdown(); + } + } + + @Test + public void testUnknownHost_withCallbacks() throws Exception { + unknownHost_withCallbacks(); + assertUnknownHostExternal(); + } + + @Trace(dispatcher = true) + private void unknownHost_withCallbacks() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/", ar -> { + if (ar.failed()) { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + } + }); + latch.await(); + } + + @Test + public void testUnknownHost_withReturnedFuture() throws Exception { + unknownHost_withReturnedFuture(); + assertUnknownHostExternal(); + } + + @Trace(dispatcher = true) + private void unknownHost_withReturnedFuture() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + + Future r = httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/"); + r.onFailure(err -> { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + }); + + latch.await(); + } + + private void assertUnknownHostExternal() { + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(250)); + + final String txn = introspector.getTransactionNames().iterator().next(); + assertNotNull("Transaction not found", txn); + + assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/handleResponse")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/handleResponse")); + + // Unknown hosts generate no external rollups + assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/all")); + + // Make sure exception handler is linked + final TransactionEvent event = introspector.getTransactionEvents(txn).iterator().next(); + assertTrue(event.getAttributes().containsKey("exceptionHandler")); + } + + private void assertExternal(String transactionName, String host) { + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Collection externalRequests = introspector.getExternalRequests(transactionName); + ExternalRequest request = externalRequests.iterator().next(); + assertEquals(host, request.getHostname()); + assertEquals("Vertx-Client", request.getLibrary()); + assertEquals("handleResponse", request.getOperation()); + Collection events = introspector.getTransactionEvents(transactionName); + TransactionEvent event = events.iterator().next(); + assertTrue(event.getAttributes().containsKey("responseHandler")); + + assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/handleResponse")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/handleResponse")); + + Collection transactionEvents = introspector.getTransactionEvents(transactionName); + assertEquals(1, transactionEvents.size()); + TransactionEvent transactionEvent = transactionEvents.iterator().next(); + assertEquals(1, transactionEvent.getExternalCallCount()); + assertTrue(transactionEvent.getExternalDurationInSec() > 0); + + // external rollups + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + private static int getAvailablePort() { + int port; + + try { + ServerSocket socket = new ServerSocket(0); + port = socket.getLocalPort(); + socket.close(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral port"); + } + return port; + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java new file mode 100644 index 0000000000..f3676b8fd0 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java @@ -0,0 +1,200 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.CompositeFuture; +import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxFuture { + + @Test + public void testCompositeFuture() throws InterruptedException { + compositeFuturesAllFuturesSucceed(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFuturesAllFuturesSucceed"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("compositeFuture")); + } + + @Trace(dispatcher = true) + private void compositeFuturesAllFuturesSucceed() throws InterruptedException { + Vertx vertx = Vertx.vertx(); + CountDownLatch latch = new CountDownLatch(1); + Promise promise1 = Promise.promise(); + Promise promise2 = Promise.promise(); + + CompositeFuture.all(promise1.future(), promise2.future()).onComplete((ar -> { + if (ar.succeeded()) { + NewRelic.addCustomParameter("compositeFuture", "yes"); + } + latch.countDown(); + })); + + vertx.setTimer(1, handler -> { + promise1.complete("promise1"); + promise2.complete("promise2"); + }); + + latch.await(); + } + + @Test + public void whenFutureFails_withThrowable_txnStillCompletes() throws InterruptedException { + failFutureWithThrowable(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithThrowable"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureFails_withString_txnStillCompletes() throws InterruptedException { + failFutureWithString(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithString"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureCompletes_txnStillCompletes() throws InterruptedException { + completeFutureSuccessfully(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfully"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureCompletes_withOnCompleteRegistered_txnStillCompletes() throws InterruptedException { + completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Trace(dispatcher = true) + private void failFutureWithString() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onFailure(ar -> { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.fail("oops"); + }); + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void failFutureWithThrowable() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onFailure(ar -> { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.fail(new IllegalArgumentException("foo")); + }); + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void completeFutureSuccessfully() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onSuccess(ar -> { + NewRelic.addCustomParameter("future", "success"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.complete("hooray"); + }); + + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onComplete(ar -> { + NewRelic.addCustomParameter("future", "onComplete"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.complete("hooray"); + }); + + countDownLatch.await(); + } +} diff --git a/settings.gradle b/settings.gradle index 4b0412c6fe..9e9190036a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -380,6 +380,7 @@ include 'instrumentation:vertx-core-3.6.0' include 'instrumentation:vertx-core-3.8.0' include 'instrumentation:vertx-core-3.9.0' include 'instrumentation:vertx-core-4.0.0' +include 'instrumentation:vertx-core-4.1.0' include 'instrumentation:vertx-core-4.3.2' include 'instrumentation:zio' From e543c0a29b6583be2c996cc2031bbc92f8380254 Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Tue, 31 Oct 2023 07:34:38 -0400 Subject: [PATCH 06/16] WIP commit --- instrumentation/vertx-core-4.0.0/.gitignore | 42 ++ instrumentation/vertx-core-4.0.0/build.gradle | 25 ++ .../instrumentation/AsyncHandlerWrapper.java | 37 ++ .../vertx/instrumentation/InboundWrapper.java | 32 ++ .../instrumentation/OutboundWrapper.java | 32 ++ .../PromiseHandlerWrapper.java | 37 ++ .../vertx/instrumentation/VertxCoreUtil.java | 72 ++++ ...HttpClientRequestBase_Instrumentation.java | 107 +++++ ...HttpClientRequestImpl_Instrumentation.java | 74 ++++ .../impl/AbstractContext_Instrumentation.java | 24 ++ .../impl/ContextImpl_Instrumentation.java | 25 ++ .../future/FutureImpl_Instrumentation.java | 84 ++++ .../instrumentation/VertxBlockingTest.java | 59 +++ .../nr/vertx/instrumentation/VertxClient.java | 379 ++++++++++++++++++ .../nr/vertx/instrumentation/VertxFuture.java | 200 +++++++++ instrumentation/vertx-core-4.3.2/.gitignore | 42 ++ instrumentation/vertx-core-4.3.2/build.gradle | 25 ++ .../vertx/instrumentation/InboundWrapper.java | 32 ++ .../instrumentation/OutboundWrapper.java | 32 ++ .../vertx/instrumentation/VertxCoreUtil.java | 71 ++++ ...HttpClientRequestBase_Instrumentation.java | 64 +++ ...HttpClientRequestImpl_Instrumentation.java | 64 +++ .../impl/ContextBase_Instrumentation.java | 36 ++ .../core/impl/FutureImpl_Instrumentation.java | 48 +++ .../instrumentation/VertxBlockingTest.java | 60 +++ .../nr/vertx/instrumentation/VertxClient.java | 348 ++++++++++++++++ .../nr/vertx/instrumentation/VertxFuture.java | 105 +++++ .../main/java/com/newrelic/agent/Segment.java | 2 + .../java/com/newrelic/agent/TokenImpl.java | 1 + .../java/com/newrelic/agent/Transaction.java | 1 + .../newrelic/agent/TransactionApiImpl.java | 3 + settings.gradle | 2 + 32 files changed, 2165 insertions(+) create mode 100644 instrumentation/vertx-core-4.0.0/.gitignore create mode 100644 instrumentation/vertx-core-4.0.0/build.gradle create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java create mode 100644 instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java create mode 100644 instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java create mode 100644 instrumentation/vertx-core-4.3.2/.gitignore create mode 100644 instrumentation/vertx-core-4.3.2/build.gradle create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java create mode 100644 instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java create mode 100644 instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java diff --git a/instrumentation/vertx-core-4.0.0/.gitignore b/instrumentation/vertx-core-4.0.0/.gitignore new file mode 100644 index 0000000000..b63da4551b --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/instrumentation/vertx-core-4.0.0/build.gradle b/instrumentation/vertx-core-4.0.0/build.gradle new file mode 100644 index 0000000000..e17b5f6b24 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/build.gradle @@ -0,0 +1,25 @@ +jar { + manifest { + attributes 'Implementation-Title': 'com.newrelic.instrumentation.vertx-core-4.0.0' + } +} + + + +dependencies { + implementation(project(":agent-bridge")) + implementation("io.vertx:vertx-core:4.0.0") + testImplementation("io.vertx:vertx-core:4.0.0") +} + +verifyInstrumentation { + passesOnly 'io.vertx:vertx-core:[4.0.0,4.3.2)' + excludeRegex '.*CR[0-9]*' + excludeRegex '.*-milestone[0-9]' + excludeRegex '.*Beta[0-9]' +} + +site { + title 'Vertx' + type 'Framework' +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java new file mode 100644 index 0000000000..3a31ccd31c --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java @@ -0,0 +1,37 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; + +public class AsyncHandlerWrapper implements Handler> { + private Handler> original; + + private Token token; + + public AsyncHandlerWrapper(Handler> original, Token token) { + System.out.println("------- Construct AsyncHandlerWrapper "+ token); + this.original = original; + this.token = token; + } + + @Override + @Trace(async = true, excludeFromTransactionTrace = true) + public void handle(AsyncResult event) { + if (token != null) { + System.out.println("------- Link&Expire " + this.token); + token.linkAndExpire(); + token = null; + } + + this.original.handle(event); + this.original = null; + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java new file mode 100644 index 0000000000..41531affba --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.InboundHeaders; +import io.vertx.core.http.HttpClientResponse; + +public class InboundWrapper implements InboundHeaders { + + private final HttpClientResponse response; + + public InboundWrapper(HttpClientResponse response) { + this.response = response; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public String getHeader(String name) { + return response.getHeader(name); + } + +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java new file mode 100644 index 0000000000..7f4765a262 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.OutboundHeaders; +import io.vertx.core.MultiMap; + +public class OutboundWrapper implements OutboundHeaders { + + private final MultiMap headers; + + public OutboundWrapper(MultiMap headers) { + this.headers = headers; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public void setHeader(String name, String value) { + headers.add(name, value); + } + +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java new file mode 100644 index 0000000000..2b57204455 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java @@ -0,0 +1,37 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Handler; +import io.vertx.core.Promise; + +public class PromiseHandlerWrapper implements Handler> { + + private Handler> original; + + private Token token; + + public PromiseHandlerWrapper(Handler> original, Token token) { + System.out.println("wrapped constr " + token); + this.original = original; + this.token = token; + } + + @Override + @Trace(async = true, excludeFromTransactionTrace = true) + public void handle(Promise event) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + this.original.handle(event); + this.original = null; + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java new file mode 100644 index 0000000000..e511bbcb17 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java @@ -0,0 +1,72 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.GenericParameters; +import com.newrelic.api.agent.HttpParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.weaver.Weaver; +import io.vertx.core.Handler; +import io.vertx.core.http.impl.HttpClientResponseImpl; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; + +public class VertxCoreUtil { + + private VertxCoreUtil() { + } + + private static final Map tokenMap = AgentBridge.collectionFactory.createConcurrentWeakKeyedMap(); + + public static final String VERTX_CLIENT = "Vertx-Client"; + public static final String END = "handleResponse"; + + private static final URI UNKNOWN_HOST_URI = URI.create("http://UnknownHost/"); + + public static void storeToken(Handler handler) { + if (handler != null && AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("storeToken --- "); + tokenMap.put(handler, NewRelic.getAgent().getTransaction().getToken()); + } + } + + public static void linkAndExpireToken(Handler handler) { + if (handler != null) { + final Token token = tokenMap.remove(handler); + if (token != null) { + token.linkAndExpire(); + } + } + } + + public static void processResponse(Segment segment, HttpClientResponseImpl resp, String host, int port, + String scheme) { + try { + URI uri = new URI(scheme, null, host, port, null, null, null); + segment.reportAsExternal(HttpParameters.library(VERTX_CLIENT) + .uri(uri) + .procedure(END) + .inboundHeaders(new InboundWrapper(resp)) + .build()); + } catch (URISyntaxException e) { + AgentBridge.instrumentation.noticeInstrumentationError(e, Weaver.getImplementationTitle()); + } + } + + public static void reportUnknownHost(Segment segment) { + segment.reportAsExternal(GenericParameters.library(VERTX_CLIENT) + .uri(UNKNOWN_HOST_URI) + .procedure(END) + .build()); + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java new file mode 100644 index 0000000000..cf996bf7a4 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -0,0 +1,107 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.http.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.Transaction; +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.vertx.instrumentation.AsyncHandlerWrapper; +import com.nr.vertx.instrumentation.OutboundWrapper; +import com.nr.vertx.instrumentation.VertxCoreUtil; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.MultiMap; +import io.vertx.core.Promise; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; + +import java.net.UnknownHostException; +import java.util.logging.Level; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +@Weave(type = MatchType.BaseClass, originalName = "io.vertx.core.http.impl.HttpClientRequestBase") +public abstract class HttpClientRequestBase_Instrumentation { + + @NewField + public Segment segment; + + @NewField + public Token token; + + public abstract MultiMap headers(); + + @Trace(async = true) + public HttpClientRequest response(Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response " + handler); + if (AgentBridge.getAgent().getTransaction(false) != null) { + this.token = NewRelic.getAgent().getTransaction().getToken(); + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + + return Weaver.callOriginal(); + } + + @Trace(async = true) + void handleResponse(Promise promise, HttpClientResponse resp, long timeoutMs) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse()"); + System.out.println("----------- linkAndExpire " + this.token.link() + " " + this.token); + if (segment != null) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse() segment != null"); + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + Token segmentToken = segment.getTransaction().getToken(); + System.out.println("segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segmentToken); + reportExternal(resp, segment); + + segment.end(); + System.out.println("2222222222222 link " + segmentToken.link()); + System.out.println("33333 expire " + segmentToken.expire()); + } + this.token.expire(); + this.token = null; + Transaction t1 = AgentBridge.getAgent().getTransaction(); + Transaction t2 = NewRelic.getAgent().getTransaction(); + + Weaver.callOriginal(); + } + + @Trace(async = true) + void handleException(Throwable t) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleException()"); + if (segment != null) { + if (t instanceof UnknownHostException) { + VertxCoreUtil.reportUnknownHost(segment); + } + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + } + Weaver.callOriginal(); + } + + private void reportExternal(HttpClientResponse response, Segment segment) { + if (response instanceof HttpClientResponseImpl) { + HttpClientResponseImpl resp = (HttpClientResponseImpl) response; + final String host = resp.request().getHost(); + final int port = resp.request().getPort(); + final String scheme = resp.request().ssl ? "https" : "http"; + VertxCoreUtil.processResponse(segment, resp, host, port, scheme); + } + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java new file mode 100644 index 0000000000..c0a9af31f7 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java @@ -0,0 +1,74 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.http.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.OutboundWrapper; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; + +import java.util.logging.Level; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +@Weave(originalName = "io.vertx.core.http.impl.HttpClientRequestImpl") +public abstract class HttpClientRequestImpl_Instrumentation extends HttpClientRequestBase_Instrumentation { + +// public Future end(Buffer chunk) { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk"); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk 2"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// } +// return Weaver.callOriginal(); +// } +// +// public void end(Buffer chunk, Handler> handler) { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler"); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler 2"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// } +// Weaver.callOriginal(); +// } +// +// public Future end() { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end()"); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end() 2"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// System.out.println("end() Segment -- " + segment); +// } +// return Weaver.callOriginal(); +// } +// +// public void end(Handler> handler) { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler)"); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler) 2"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// System.out.println("!!!!segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segment.getTransaction().getToken()); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// System.out.println("end(Handler> Segment -- " + segment); +// } +// Weaver.callOriginal(); +// } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java new file mode 100644 index 0000000000..c5630d9a81 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java @@ -0,0 +1,24 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package io.vertx.core.impl; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.AsyncHandlerWrapper; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; + +@Weave(originalName = "io.vertx.core.impl.AbstractContext") +abstract class AbstractContext_Instrumentation { + private static void setResultHandler(ContextInternal ctx, Future fut, Handler> resultHandler) { + System.out.println("setResulthandler ----"); + resultHandler = new AsyncHandlerWrapper<>(resultHandler, NewRelic.getAgent().getTransaction().getToken()); + Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java new file mode 100644 index 0000000000..500a580c3d --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java @@ -0,0 +1,25 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.impl; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.PromiseHandlerWrapper; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.Promise; + +@Weave(originalName = "io.vertx.core.impl.ContextImpl") +abstract class ContextImpl_Instrumentation { + static Future executeBlocking(ContextInternal context, Handler> blockingCodeHandler, WorkerPool workerPool, TaskQueue queue) { + System.out.println("executeBlocking ----"); + blockingCodeHandler = new PromiseHandlerWrapper(blockingCodeHandler, NewRelic.getAgent().getTransaction().getToken()); + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java new file mode 100644 index 0000000000..251b351b99 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java @@ -0,0 +1,84 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.impl.future; + +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.vertx.instrumentation.VertxCoreUtil; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; + +@Weave(originalName = "io.vertx.core.impl.future.FutureImpl") +abstract class FutureImpl_Instrumentation { + + @NewField + private Handler handler = null; + private Listener listener; + + public abstract boolean isComplete(); + + @Trace(async = true, excludeFromTransactionTrace = true) + public void addListener(Listener listener) { + if (isComplete()) { + VertxCoreUtil.linkAndExpireToken(listener); + } else { + VertxCoreUtil.storeToken(listener); + } +// if (!isComplete()) { +// System.out.println("addListener " + " " + this.listener + " " + listener + " " + System.identityHashCode(listener) + " " + System.currentTimeMillis()); +// assignAndStoreHandlerInstance(listener); +// } + Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public Future onSuccess(Handler handler) { + System.out.println("success"); + //assignAndStoreHandlerInstance(handler); + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public Future onComplete(Handler handler) { + System.out.println("complete"); + //assignAndStoreHandlerInstance(handler); + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public Future onFailure(Handler handler) { + //assignAndStoreHandlerInstance(handler); + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryComplete(Object result) { + System.out.println("trycomplete " + " " + listener + " " + System.identityHashCode(this) + " " + System.currentTimeMillis()); + VertxCoreUtil.linkAndExpireToken(this.listener); + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryFail(Throwable cause) { + System.out.println("tryFail"); + VertxCoreUtil.linkAndExpireToken(this.listener); + return Weaver.callOriginal(); + } + + private void assignAndStoreHandlerInstance(Listener listener) { + VertxCoreUtil.storeToken(listener); + } + +// private void assignAndStoreHandlerInstance(Handler listener) { +// VertxCoreUtil.storeToken(listener); +// } + +} diff --git a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java new file mode 100644 index 0000000000..0fa7ac126a --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java @@ -0,0 +1,59 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Vertx; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxBlockingTest { + + @Test + public void executeBlocking_withPromiseAndResult() throws InterruptedException { + Vertx vertx = Vertx.vertx(); + try { + executeBlocking(vertx); + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxBlockingTest/executeBlocking"; + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("InFuture")); + assertTrue(attributes.containsKey("InResponseHandler")); + } finally { + vertx.close(); + } + } + + @Trace(dispatcher = true) + void executeBlocking(Vertx vertx) throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + vertx.executeBlocking(future -> { + NewRelic.addCustomParameter("InFuture", "yes"); + future.complete(); + }, res -> { + NewRelic.addCustomParameter("InResponseHandler", "yes"); + countDownLatch.countDown(); + }); + countDownLatch.await(); + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java new file mode 100644 index 0000000000..2aaa25e9d1 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java @@ -0,0 +1,379 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.CatHelper; +import com.newrelic.agent.introspec.ExternalRequest; +import com.newrelic.agent.introspec.HttpTestServer; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.MetricsHelper; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.agent.introspec.internal.HttpServerLocator; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.Collection; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxClient { + + private static int port; + private static Future server; + private static Vertx vertx; + + @BeforeClass + public static void beforeClass() { + port = getAvailablePort(); + vertx = Vertx.vertx(); + + server = vertx.createHttpServer().requestHandler(request -> { + final String statusCode = request.getHeader("statusCode"); + if (statusCode == null) { + System.out.println("statusCode is null"); + request.response().end("response"); + } else { + System.out.println("statusCode is NOT null -- " + statusCode); + if (request.absoluteURI().equals("/redirect")) { + request.headers().clear(); + request.response().putHeader("Location", "http://localhost:" + port + "/other"); + } + request.response().setStatusCode(Integer.parseInt(statusCode)).end("response"); + } + }).listen(port); + } + + @AfterClass + public static void afterClass() { + server.result().close(); + vertx.close(); + } + + @Test + public void testGet() throws InterruptedException { + getCall(); + // Wait for transaction to finish + System.out.println("testGet " + NewRelic.getAgent().getTransaction()); + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall", "localhost"); + } + + @Trace(dispatcher = true) + public void getCall() throws InterruptedException { + System.out.println("dispatcher -- " + NewRelic.getAgent().getTransaction()); + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + //httpClient.close(); + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request, waiting for response in ar2 + if (respAsyncResult.succeeded()) { + System.out.println("respAsyncResult succeed"); + HttpClientResponse response = respAsyncResult.result(); + int statusCode = response.statusCode(); + latch.countDown(); + System.out.println("response: " + response + " " + statusCode); + response.body(respBufferAsyncResult -> { //Retriev response + if (respBufferAsyncResult.succeeded()) { + Buffer body = respBufferAsyncResult.result(); + // Handle body entirely + } else { + // Handle server error, for example, connection closed + } + }); + } else { + // Handle server error, for example, connection closed + } + }); + } else { + // Connection error, for example, invalid server or invalid SSL certificate + } + }); + latch.await(); + + System.out.println("testGet " + NewRelic.getAgent().getTransaction()); + } + + @Test + public void testGetNow() throws InterruptedException { + getNowCall(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getNowCall", "localhost"); + } + + @Trace(dispatcher = true) + private void getNowCall() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + //httpClient.getNow(port, "localhost", "/", requestHandler(latch)); + latch.await(); + } + + @Test + public void testPost() throws InterruptedException { + postCall(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/postCall", "localhost"); + } + + @Trace(dispatcher = true) + private void postCall() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + //httpClient.post(port, "localhost", "/", requestHandler(latch)).end(); + latch.await(); + } + + @Test + public void testEndMethods() throws InterruptedException { + endMethods(); + assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); + final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( + "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/endMethods").iterator().next(); + assertEquals(4, externalRequest.getCount()); + assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + @Trace(dispatcher = true) + public void endMethods() throws InterruptedException { + HttpClient httpClient = vertx.createHttpClient(); + CountDownLatch latch = new CountDownLatch(4); + + Buffer bufferChunk = Buffer.buffer("buffer chunk!"); + String stringChunk = "string chunk!"; + String encoding = "UTF-8"; + + // tests the various overloaded versions of the end method + //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); + //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(bufferChunk); + //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk, encoding); + //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk); + latch.await(); + } + + @Test + public void testMethods() throws InterruptedException { + requestMethods(); + assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); + final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( + "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/requestMethods").iterator().next(); + assertEquals(5, externalRequest.getCount()); + assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + @Trace(dispatcher = true) + public void requestMethods() throws InterruptedException { + HttpClient httpClient = vertx.createHttpClient(); + CountDownLatch latch = new CountDownLatch(5); + //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); + //httpClient.request(HttpMethod.POST, port, "localhost", "/hi", requestHandler(latch)).end(); + //httpClient.request(HttpMethod.PUT, port, "localhost", "/hi", requestHandler(latch)).end(); + //httpClient.request(HttpMethod.HEAD, port, "localhost", "/hi", requestHandler(latch)).end(); + //httpClient.request(HttpMethod.DELETE, port, "localhost", "/hi", requestHandler(latch)).end(); + latch.await(); + } + + @Test + public void testRedirect() throws InterruptedException { + redirect(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/redirect", "localhost"); + } + + @Trace(dispatcher = true) + public void redirect() throws InterruptedException { + HttpClient httpClient = vertx.createHttpClient(); + CountDownLatch latch = new CountDownLatch(1); + //httpClient.get(port, "localhost", "/redirect") + // .putHeader("statusCode", "301") + // .setFollowRedirects(true) + // .handler(requestHandler(latch)) + // .end(); + latch.await(); + } + + @Test + public void testCat() throws Exception { + try (HttpTestServer httpServer = HttpServerLocator.createAndStart()) { + cat(httpServer); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + String host = httpServer.getEndPoint().getHost(); + String txName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/cat"; + assertEquals(2, introspector.getFinishedTransactionCount(250)); + Collection names = introspector.getTransactionNames(); + assertEquals(2, names.size()); + assertTrue(names.contains(httpServer.getServerTransactionName())); + assertTrue(names.contains(txName)); + + // scoped metrics + assertEquals(1, MetricsHelper.getScopedMetricCount(txName, "ExternalTransaction/" + host + "/" + + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); + assertEquals(1, MetricsHelper.getScopedMetricCount(txName, + "Java/com.nr.vertx.instrumentation.VertxClient/cat")); + + // unscoped metrics + assertEquals(1, MetricsHelper.getUnscopedMetricCount("ExternalTransaction/" + host + "/" + + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/" + host + "/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); + + // events + Collection transactionEvents = introspector.getTransactionEvents(txName); + assertEquals(1, transactionEvents.size()); + TransactionEvent transactionEvent = transactionEvents.iterator().next(); + assertEquals(1, transactionEvent.getExternalCallCount()); + assertTrue(transactionEvent.getExternalDurationInSec() > 0); + + CatHelper.verifyOneSuccessfulCat(introspector, txName); + + // external request information + Collection externalRequests = introspector.getExternalRequests(txName); + assertEquals(1, externalRequests.size()); + ExternalRequest externalRequest = externalRequests.iterator().next(); + assertEquals(1, externalRequest.getCount()); + assertEquals(host, externalRequest.getHostname()); + } + } + + @Trace(dispatcher = true) + public void cat(HttpTestServer httpServer) throws Exception { + try { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + //httpClient.get(httpServer.getEndPoint().getPort(), httpServer.getEndPoint().getHost(), "/") + //.putHeader(HttpTestServer.DO_CAT, "true") + // .handler(requestHandler(latch)) + // .end(); + latch.await(); + } finally { + httpServer.shutdown(); + } + } + + @Test + public void testUnknownHost() throws Exception { + unknownHost(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(250)); + + final String txn = introspector.getTransactionNames().iterator().next(); + assertNotNull("Transaction not found", txn); + + assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/end")); + + // Unknown hosts generate no external rollups + assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/all")); + + // Make sure exception handler is linked + final TransactionEvent event = introspector.getTransactionEvents(txn).iterator().next(); + assertTrue(event.getAttributes().containsKey("exceptionHandler")); + } + + @Trace(dispatcher = true) + private void unknownHost() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + //httpClient.get(port, "notARealHostDuderina.com", "/") + // .exceptionHandler(exceptionHandler(latch)) + // .handler(requestHandler(null)) + // .end(); + latch.await(); + } + + private Handler exceptionHandler(CountDownLatch latch) { + return error -> { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + }; + } + + private Handler requestHandler(CountDownLatch latch) { + return response -> { + NewRelic.addCustomParameter("responseHandler", "true"); + response.bodyHandler(body -> { + NewRelic.addCustomParameter("bodyHandler", "true"); + latch.countDown(); + }); + }; + } + + public void assertExternal(String transactionName, String host) { + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Collection externalRequests = introspector.getExternalRequests(transactionName); + ExternalRequest request = externalRequests.iterator().next(); + assertEquals(host, request.getHostname()); + assertEquals("Vertx-Client", request.getLibrary()); + assertEquals("end", request.getOperation()); + Collection events = introspector.getTransactionEvents(transactionName); + TransactionEvent event = events.iterator().next(); + assertTrue(event.getAttributes().containsKey("responseHandler")); + + assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/end")); + + Collection transactionEvents = introspector.getTransactionEvents(transactionName); + assertEquals(1, transactionEvents.size()); + TransactionEvent transactionEvent = transactionEvents.iterator().next(); + assertEquals(1, transactionEvent.getExternalCallCount()); + assertTrue(transactionEvent.getExternalDurationInSec() > 0); + + // external rollups + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + private static int getAvailablePort() { + int port; + + try { + ServerSocket socket = new ServerSocket(0); + port = socket.getLocalPort(); + socket.close(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral port"); + } + return port; + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java new file mode 100644 index 0000000000..f3676b8fd0 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java @@ -0,0 +1,200 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.CompositeFuture; +import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxFuture { + + @Test + public void testCompositeFuture() throws InterruptedException { + compositeFuturesAllFuturesSucceed(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFuturesAllFuturesSucceed"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("compositeFuture")); + } + + @Trace(dispatcher = true) + private void compositeFuturesAllFuturesSucceed() throws InterruptedException { + Vertx vertx = Vertx.vertx(); + CountDownLatch latch = new CountDownLatch(1); + Promise promise1 = Promise.promise(); + Promise promise2 = Promise.promise(); + + CompositeFuture.all(promise1.future(), promise2.future()).onComplete((ar -> { + if (ar.succeeded()) { + NewRelic.addCustomParameter("compositeFuture", "yes"); + } + latch.countDown(); + })); + + vertx.setTimer(1, handler -> { + promise1.complete("promise1"); + promise2.complete("promise2"); + }); + + latch.await(); + } + + @Test + public void whenFutureFails_withThrowable_txnStillCompletes() throws InterruptedException { + failFutureWithThrowable(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithThrowable"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureFails_withString_txnStillCompletes() throws InterruptedException { + failFutureWithString(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithString"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureCompletes_txnStillCompletes() throws InterruptedException { + completeFutureSuccessfully(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfully"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureCompletes_withOnCompleteRegistered_txnStillCompletes() throws InterruptedException { + completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Trace(dispatcher = true) + private void failFutureWithString() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onFailure(ar -> { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.fail("oops"); + }); + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void failFutureWithThrowable() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onFailure(ar -> { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.fail(new IllegalArgumentException("foo")); + }); + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void completeFutureSuccessfully() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onSuccess(ar -> { + NewRelic.addCustomParameter("future", "success"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.complete("hooray"); + }); + + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onComplete(ar -> { + NewRelic.addCustomParameter("future", "onComplete"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.complete("hooray"); + }); + + countDownLatch.await(); + } +} diff --git a/instrumentation/vertx-core-4.3.2/.gitignore b/instrumentation/vertx-core-4.3.2/.gitignore new file mode 100644 index 0000000000..b63da4551b --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/instrumentation/vertx-core-4.3.2/build.gradle b/instrumentation/vertx-core-4.3.2/build.gradle new file mode 100644 index 0000000000..d3bd7ec4a5 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/build.gradle @@ -0,0 +1,25 @@ +jar { + manifest { + attributes 'Implementation-Title': 'com.newrelic.instrumentation.vertx-core-4.3.2' + } +} + + + +dependencies { + implementation(project(":agent-bridge")) + implementation("io.vertx:vertx-core:4.3.2") + testImplementation("io.vertx:vertx-core:4.0.0") +} + +verifyInstrumentation { + passesOnly 'io.vertx:vertx-core:[4.3.2,)' + excludeRegex '.*CR[0-9]*' + excludeRegex '.*-milestone[0-9]' + excludeRegex '.*Beta[0-9]' +} + +site { + title 'Vertx' + type 'Framework' +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java new file mode 100644 index 0000000000..e7289a87b0 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.InboundHeaders; +import io.vertx.core.http.HttpClientResponse; + +public class InboundWrapper implements InboundHeaders { + + private final HttpClientResponse response; + + public InboundWrapper(HttpClientResponse response) { + this.response = response; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public String getHeader(String name) { + return response.getHeader(name); + } + +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java new file mode 100644 index 0000000000..0bc00583ca --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.OutboundHeaders; +import io.vertx.core.MultiMap; + +public class OutboundWrapper implements OutboundHeaders { + + private final MultiMap headers; + + public OutboundWrapper(MultiMap headers) { + this.headers = headers; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public void setHeader(String name, String value) { + headers.add(name, value); + } + +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java new file mode 100644 index 0000000000..b48dd349ae --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java @@ -0,0 +1,71 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.GenericParameters; +import com.newrelic.api.agent.HttpParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.weaver.Weaver; +import io.vertx.core.Handler; +import io.vertx.core.http.impl.HttpClientResponseImpl; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; + +public class VertxCoreUtil { + + private VertxCoreUtil() { + } + + private static final Map tokenMap = AgentBridge.collectionFactory.createConcurrentWeakKeyedMap(); + + public static final String VERTX_CLIENT = "Vertx-Client"; + public static final String END = "end"; + + private static URI UNKNOWN_HOST_URI = URI.create("http://UnknownHost/"); + + public static void storeToken(Handler handler) { + if (handler != null && AgentBridge.getAgent().getTransaction(false) != null) { + tokenMap.put(handler, NewRelic.getAgent().getTransaction().getToken()); + } + } + + public static void linkAndExpireToken(Handler handler) { + if (handler != null) { + final Token token = tokenMap.remove(handler); + if (token != null) { + token.linkAndExpire(); + } + } + } + + public static void processResponse(Segment segment, HttpClientResponseImpl resp, String host, int port, + String scheme) { + try { + URI uri = new URI(scheme, null, host, port, null, null, null); + segment.reportAsExternal(HttpParameters.library(VERTX_CLIENT) + .uri(uri) + .procedure(END) + .inboundHeaders(new InboundWrapper(resp)) + .build()); + } catch (URISyntaxException e) { + AgentBridge.instrumentation.noticeInstrumentationError(e, Weaver.getImplementationTitle()); + } + } + + public static void reportUnknownHost(Segment segment) { + segment.reportAsExternal(GenericParameters.library(VERTX_CLIENT) + .uri(UNKNOWN_HOST_URI) + .procedure(END) + .build()); + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java new file mode 100644 index 0000000000..3cb00268f0 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -0,0 +1,64 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.http.impl; + +import com.newrelic.api.agent.Segment; +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.vertx.instrumentation.VertxCoreUtil; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpClientResponse; + +import java.net.UnknownHostException; + +@Weave(originalName = "io.vertx.core.http.impl.HttpClientRequestBase") +public abstract class HttpClientRequestBase_Instrumentation { + + @NewField + public Segment segment; + + public abstract MultiMap headers(); + + @Trace(async = true) + void handleResponse(HttpClientResponse resp) { + if (segment != null) { + reportExternal(resp, segment); + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + } + + Weaver.callOriginal(); + } + + @Trace(async = true) + void handleException(Throwable t) { + if (segment != null) { + if (t instanceof UnknownHostException) { + VertxCoreUtil.reportUnknownHost(segment); + } + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + } + Weaver.callOriginal(); + } + + private void reportExternal(HttpClientResponse response, Segment segment) { + if (response instanceof HttpClientResponseImpl) { + HttpClientResponseImpl resp = (HttpClientResponseImpl) response; + final String host = resp.request().getHost(); + final int port = resp.request().getPort(); + final String scheme = resp.request().ssl ? "https" : "http"; + VertxCoreUtil.processResponse(segment, resp, host, port, scheme); + } + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java new file mode 100644 index 0000000000..54b6612f56 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java @@ -0,0 +1,64 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.http.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.OutboundWrapper; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +@Weave(originalName = "io.vertx.core.http.impl.HttpClientRequestImpl") +public abstract class HttpClientRequestImpl_Instrumentation extends HttpClientRequestBase_Instrumentation { + + public Future end(Buffer chunk) { + if (AgentBridge.getAgent().getTransaction(false) != null) { + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + Weaver.callOriginal(); + + //hack + return null; + } + + public void end(Buffer chunk, Handler> handler) { + if (AgentBridge.getAgent().getTransaction(false) != null) { + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + Weaver.callOriginal(); + } + + public Future end() { + if (AgentBridge.getAgent().getTransaction(false) != null) { + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + Weaver.callOriginal(); + + //hack + return null; + } + + public void end(Handler> handler) { + if (AgentBridge.getAgent().getTransaction(false) != null) { + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + Weaver.callOriginal(); + } + +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java new file mode 100644 index 0000000000..f896ac0976 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java @@ -0,0 +1,36 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.impl; + +import com.newrelic.api.agent.weaver.Weave; + +@Weave(originalName = "io.vertx.core.impl.ContextBase") +public abstract class ContextBase_Instrumentation { + + //static Future executeBlocking(ContextInternal context, Handler> blockingCodeHandler, WorkerPool workerPool, TaskQueue queue) + +// void executeBlocking(Handler> blockingCodeHandler, Handler resultHandler, +// Executor exec, TaskQueue queue, PoolMetrics metrics) { +// VertxCoreUtil.storeToken(blockingCodeHandler); +// VertxCoreUtil.storeToken(resultHandler); +// Weaver.callOriginal(); +// } +// +// @Trace(async = true) +// private void lambda$executeBlocking$2(PoolMetrics metricsHandler, Object object, Handler handler, Handler resultHandler) { +// VertxCoreUtil.linkAndExpireToken(handler); +// Weaver.callOriginal(); +// } +// +// @Trace(async = true) +// private static void lambda$null$0(Handler handler, AsyncResult result, Void v) { +// VertxCoreUtil.linkAndExpireToken(handler); +// Weaver.callOriginal(); +// } + +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java new file mode 100644 index 0000000000..9266fcee33 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java @@ -0,0 +1,48 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.impl; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.VertxCoreUtil; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; + +@Weave(originalName = "io.vertx.core.impl.future.FutureImpl") +abstract class FutureImpl_Instrumentation { + + //private Handler handler = Weaver.callOriginal(); + + //to use concrete method in instrumented class + public abstract boolean isComplete(); + + + @Trace(async = true, excludeFromTransactionTrace = true) + public Future onComplete(Handler handler) { + if (isComplete()) { + VertxCoreUtil.linkAndExpireToken(handler); + } else { + VertxCoreUtil.storeToken(handler); + } + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryComplete(Object result) { + //VertxCoreUtil.linkAndExpireToken(handler); + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryFail(Throwable cause) { + //VertxCoreUtil.linkAndExpireToken(handler); + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java new file mode 100644 index 0000000000..622d18e349 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java @@ -0,0 +1,60 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Vertx; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxBlockingTest { + + @Test + public void testBlocking() throws InterruptedException { + Vertx vertx = Vertx.vertx(); + try { + executeBlocking(vertx); + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxBlockingTest/executeBlocking"; + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("InFuture")); + assertTrue(attributes.containsKey("InResponseHandler")); + } finally { + vertx.close(); + } + } + + @Trace(dispatcher = true) + void executeBlocking(Vertx vertx) throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + vertx.executeBlocking(future -> { + NewRelic.addCustomParameter("InFuture", "yes"); + future.complete(); + }, res -> { + NewRelic.addCustomParameter("InResponseHandler", "yes"); + countDownLatch.countDown(); + }); + countDownLatch.await(); + } + +} diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java new file mode 100644 index 0000000000..9e48e5905c --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java @@ -0,0 +1,348 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.CatHelper; +import com.newrelic.agent.introspec.ExternalRequest; +import com.newrelic.agent.introspec.HttpTestServer; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.MetricsHelper; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.agent.introspec.internal.HttpServerLocator; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.Collection; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxClient { + +/* + private static int port; + private static HttpServer server; + private static Vertx vertx; + + @BeforeClass + public static void beforeClass() { + port = getAvailablePort(); + vertx = Vertx.vertx(); + server = vertx.createHttpServer().requestHandler(request -> { + final String statusCode = request.getHeader("statusCode"); + if (statusCode == null) { + request.response().end("response"); + } else { + if (request.absoluteURI().equals("/redirect")) { + request.headers().clear(); + request.response().putHeader("Location", "http://localhost:" + port + "/other"); + } + request.response().setStatusCode(Integer.parseInt(statusCode)).end("response"); + } + }).listen(port); + } + + @AfterClass + public static void afterClass() { + server.close(); + vertx.close(); + } + + @Test + public void testGet() throws InterruptedException { + getCall(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall", "localhost"); + } + + @Trace(dispatcher = true) + public void getCall() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + HttpClientRequest request = httpClient.options(port, "localhost", "/").handler(requestHandler(latch)); + request.end(); + latch.await(); + } + + @Test + public void testGetNow() throws InterruptedException { + getNowCall(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getNowCall", "localhost"); + } + + @Trace(dispatcher = true) + private void getNowCall() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.getNow(port, "localhost", "/", requestHandler(latch)); + latch.await(); + } + + @Test + public void testPost() throws InterruptedException { + postCall(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/postCall", "localhost"); + } + + @Trace(dispatcher = true) + private void postCall() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.post(port, "localhost", "/", requestHandler(latch)).end(); + latch.await(); + } + + @Test + public void testEndMethods() throws InterruptedException { + endMethods(); + assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); + final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( + "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/endMethods").iterator().next(); + assertEquals(4, externalRequest.getCount()); + assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + @Trace(dispatcher = true) + public void endMethods() throws InterruptedException { + HttpClient httpClient = vertx.createHttpClient(); + CountDownLatch latch = new CountDownLatch(4); + + Buffer bufferChunk = Buffer.buffer("buffer chunk!"); + String stringChunk = "string chunk!"; + String encoding = "UTF-8"; + + // tests the various overloaded versions of the end method + httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(bufferChunk); + httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk, encoding); + httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk); + latch.await(); + } + + @Test + public void testMethods() throws InterruptedException { + requestMethods(); + assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); + final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( + "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/requestMethods").iterator().next(); + assertEquals(5, externalRequest.getCount()); + assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + @Trace(dispatcher = true) + public void requestMethods() throws InterruptedException { + HttpClient httpClient = vertx.createHttpClient(); + CountDownLatch latch = new CountDownLatch(5); + httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.POST, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.PUT, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.HEAD, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.DELETE, port, "localhost", "/hi", requestHandler(latch)).end(); + latch.await(); + } + + @Test + public void testRedirect() throws InterruptedException { + redirect(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/redirect", "localhost"); + } + + @Trace(dispatcher = true) + public void redirect() throws InterruptedException { + HttpClient httpClient = vertx.createHttpClient(); + CountDownLatch latch = new CountDownLatch(1); + httpClient.get(port, "localhost", "/redirect") + .putHeader("statusCode", "301") + .setFollowRedirects(true) + .handler(requestHandler(latch)) + .end(); + latch.await(); + } + + @Test + public void testCat() throws Exception { + try (HttpTestServer httpServer = HttpServerLocator.createAndStart()) { + cat(httpServer); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + String host = httpServer.getEndPoint().getHost(); + String txName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/cat"; + assertEquals(2, introspector.getFinishedTransactionCount(250)); + Collection names = introspector.getTransactionNames(); + assertEquals(2, names.size()); + assertTrue(names.contains(httpServer.getServerTransactionName())); + assertTrue(names.contains(txName)); + + // scoped metrics + assertEquals(1, MetricsHelper.getScopedMetricCount(txName, "ExternalTransaction/" + host + "/" + + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); + assertEquals(1, MetricsHelper.getScopedMetricCount(txName, + "Java/com.nr.vertx.instrumentation.VertxClient/cat")); + + // unscoped metrics + assertEquals(1, MetricsHelper.getUnscopedMetricCount("ExternalTransaction/" + host + "/" + + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/" + host + "/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); + + // events + Collection transactionEvents = introspector.getTransactionEvents(txName); + assertEquals(1, transactionEvents.size()); + TransactionEvent transactionEvent = transactionEvents.iterator().next(); + assertEquals(1, transactionEvent.getExternalCallCount()); + assertTrue(transactionEvent.getExternalDurationInSec() > 0); + + CatHelper.verifyOneSuccessfulCat(introspector, txName); + + // external request information + Collection externalRequests = introspector.getExternalRequests(txName); + assertEquals(1, externalRequests.size()); + ExternalRequest externalRequest = externalRequests.iterator().next(); + assertEquals(1, externalRequest.getCount()); + assertEquals(host, externalRequest.getHostname()); + } + } + + @Trace(dispatcher = true) + public void cat(HttpTestServer httpServer) throws Exception { + try { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.get(httpServer.getEndPoint().getPort(), httpServer.getEndPoint().getHost(), "/") + .putHeader(HttpTestServer.DO_CAT, "true") + .handler(requestHandler(latch)) + .end(); + latch.await(); + } finally { + httpServer.shutdown(); + } + } + + @Test + public void testUnknownHost() throws Exception { + unknownHost(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(250)); + + final String txn = introspector.getTransactionNames().iterator().next(); + assertNotNull("Transaction not found", txn); + + assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/end")); + + // Unknown hosts generate no external rollups + assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/all")); + + // Make sure exception handler is linked + final TransactionEvent event = introspector.getTransactionEvents(txn).iterator().next(); + assertTrue(event.getAttributes().containsKey("exceptionHandler")); + } + + @Trace(dispatcher = true) + private void unknownHost() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.get(port, "notARealHostDuderina.com", "/") + .exceptionHandler(exceptionHandler(latch)) + .handler(requestHandler(null)) + .end(); + latch.await(); + } + + private Handler exceptionHandler(CountDownLatch latch) { + return error -> { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + }; + } + + private Handler requestHandler(CountDownLatch latch) { + return response -> { + NewRelic.addCustomParameter("responseHandler", "true"); + response.bodyHandler(body -> { + NewRelic.addCustomParameter("bodyHandler", "true"); + latch.countDown(); + }); + }; + } + + public void assertExternal(String transactionName, String host) { + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Collection externalRequests = introspector.getExternalRequests(transactionName); + ExternalRequest request = externalRequests.iterator().next(); + assertEquals(host, request.getHostname()); + assertEquals("Vertx-Client", request.getLibrary()); + assertEquals("end", request.getOperation()); + Collection events = introspector.getTransactionEvents(transactionName); + TransactionEvent event = events.iterator().next(); + assertTrue(event.getAttributes().containsKey("responseHandler")); + + assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/end")); + + Collection transactionEvents = introspector.getTransactionEvents(transactionName); + assertEquals(1, transactionEvents.size()); + TransactionEvent transactionEvent = transactionEvents.iterator().next(); + assertEquals(1, transactionEvent.getExternalCallCount()); + assertTrue(transactionEvent.getExternalDurationInSec() > 0); + + // external rollups + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + private static int getAvailablePort() { + int port; + + try { + ServerSocket socket = new ServerSocket(0); + port = socket.getLocalPort(); + socket.close(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral port"); + } + return port; + } +*/ + +} diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java new file mode 100644 index 0000000000..831f9e01d1 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java @@ -0,0 +1,105 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.CompositeFuture; +import io.vertx.core.Future; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxFuture { + + @Test + public void testCompositeFuture() throws InterruptedException { + compositeFutures(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFutures"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("success")); + } + + @Trace(dispatcher = true) + private void compositeFutures() throws InterruptedException { + final Future abc = Future.future(); + final Future def = Future.future(); + + final ExecutorService service = Executors.newSingleThreadExecutor(); + + CountDownLatch latch = new CountDownLatch(1); + CompositeFuture.all(abc, def).setHandler(result -> { + if (result.succeeded()) { + NewRelic.addCustomParameter("success", "yes"); + } + latch.countDown(); + }); + + service.submit(() -> { + abc.complete("abc"); + }); + + service.submit(() -> { + def.complete("def"); + }); + + latch.await(); + } + + @Test + public void testFutureFail() throws InterruptedException { + failFuture(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFuture"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Trace(dispatcher = true) + private void failFuture() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Future future = Future.future(); + future.setHandler(ar -> { + if (ar.failed()) { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); + } + }); + + ExecutorService service = Executors.newSingleThreadExecutor(); + service.submit(() -> { + future.fail(new RuntimeException()); + }); + + countDownLatch.await(); + } + +} diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/Segment.java b/newrelic-agent/src/main/java/com/newrelic/agent/Segment.java index bbb0d91392..a4116f02bb 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/Segment.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/Segment.java @@ -187,6 +187,8 @@ public void finish(final Throwable t) { private void finish(final Throwable t, boolean async) { if (!isFinished.getAndSet(true)) { + System.out.println("finish " + this); + markFinishTime(); final Tracer tracer = parent; diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java index 7aabd7a552..1fe9616a7e 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java @@ -58,6 +58,7 @@ public Tracer getInitiatingTracer() { @Override public boolean expire() { + System.out.println("expire --- " + this); if (active.compareAndSet(Boolean.TRUE, Boolean.FALSE)) { Transaction tx = getTransaction().getTransactionIfExists(); if (tx != null) { diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java b/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java index 1b6af54e83..ff250212a2 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java @@ -2209,6 +2209,7 @@ public Token getToken() { getMetricAggregator().incrementCounter(AgentBridge.currentApiSource.get().getSupportabilityMetric( MetricNames.SUPPORTABILITY_API_TOKEN)); + System.out.println("getToken --- " + token); return token; } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/TransactionApiImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/TransactionApiImpl.java index e6158640f2..c5054a3494 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/TransactionApiImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/TransactionApiImpl.java @@ -468,6 +468,9 @@ public Segment startSegment(String category, String segmentName) { } Segment segment = tx.startSegment(category, segmentName); + + System.out.println("startSegment " + segment); + return segment == null ? NoOpSegment.INSTANCE : segment; } diff --git a/settings.gradle b/settings.gradle index 8e1eeda8d8..108eb2dca5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -381,5 +381,7 @@ include 'instrumentation:vertx-core-3.4.1' include 'instrumentation:vertx-core-3.6.0' include 'instrumentation:vertx-core-3.8.0' include 'instrumentation:vertx-core-3.9.0' +include 'instrumentation:vertx-core-4.0.0' +include 'instrumentation:vertx-core-4.3.2' include 'instrumentation:zio' From 6e156f576c756313dc07d1fd1314d6d2aa3b9d89 Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Wed, 1 Nov 2023 16:04:02 -0400 Subject: [PATCH 07/16] WIP commit - 1st pass working --- instrumentation/vertx-core-4.0.0/README.md | 42 +++ .../instrumentation/AsyncHandlerWrapper.java | 2 - .../HttpClientRequestPromiseWrapper.java | 153 +++++++++++ .../PromiseHandlerWrapper.java | 1 - .../vertx/instrumentation/VertxCoreUtil.java | 2 +- .../impl/HttpClientImpl_Instrumentation.java | 31 +++ ...HttpClientRequestBase_Instrumentation.java | 50 ++-- ...HttpClientRequestImpl_Instrumentation.java | 80 +++--- .../impl/AbstractContext_Instrumentation.java | 8 +- .../impl/ContextImpl_Instrumentation.java | 8 +- .../future/FutureImpl_Instrumentation.java | 41 --- .../nr/vertx/instrumentation/VertxClient.java | 254 +++++++++--------- 12 files changed, 424 insertions(+), 248 deletions(-) create mode 100644 instrumentation/vertx-core-4.0.0/README.md create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java create mode 100644 instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java diff --git a/instrumentation/vertx-core-4.0.0/README.md b/instrumentation/vertx-core-4.0.0/README.md new file mode 100644 index 0000000000..36cd1a1c4d --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/README.md @@ -0,0 +1,42 @@ +Vert.x 4.00 Core Instrumentation +================================ + +### HTTP Client + +The Vert.x core instrumentation module is mainly concerned with instrumenting the low level Vert.x HTTP Client and associated Future instances. + +The instrumentation module can instrument client instances created various ways. For example, via inline callbacks: +```text + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.POST, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + HttpClientResponse response = respAsyncResult.result(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + // Handle body + } + }); + } + }); + } + }); +``` + +Or clients created that utilize Futures for response handling: +```text + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port, "localhost", "/", ar -> { + if (ar.succeeded()) { + HttpClientRequest request = ar.result(); + request.send("foo") + .onSuccess(response -> { + //Success handler + }).onFailure(err -> { + //Failure handler + }); + } + }); +``` diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java index 3a31ccd31c..00f88bfb4d 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java @@ -17,7 +17,6 @@ public class AsyncHandlerWrapper implements Handler> { private Token token; public AsyncHandlerWrapper(Handler> original, Token token) { - System.out.println("------- Construct AsyncHandlerWrapper "+ token); this.original = original; this.token = token; } @@ -26,7 +25,6 @@ public AsyncHandlerWrapper(Handler> original, Token token) { @Trace(async = true, excludeFromTransactionTrace = true) public void handle(AsyncResult event) { if (token != null) { - System.out.println("------- Link&Expire " + this.token); token.linkAndExpire(); token = null; } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java new file mode 100644 index 0000000000..f567a17e84 --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java @@ -0,0 +1,153 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.Transaction; +import io.netty.util.concurrent.Future; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.impl.ContextInternal; +import io.vertx.core.impl.future.Listener; +import io.vertx.core.impl.future.PromiseInternal; + +import java.util.function.Function; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +public class HttpClientRequestPromiseWrapper implements PromiseInternal { + + private final PromiseInternal original; + + private Token token; + + public HttpClientRequestPromiseWrapper(PromiseInternal original, Token token) { + this.original = original; + this.token = token; + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public void complete(HttpClientRequest req) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + original.complete(req); + } + + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryFail(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (t.toString().contains("UnknownHostException") && txn != null) { + Segment segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + VertxCoreUtil.reportUnknownHost(segment); + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } + + return original.tryFail(t); + } + + @Override + public boolean tryComplete(HttpClientRequest result) { + return original.tryComplete(result); + } + + @Override + public io.vertx.core.Future future() { + return original.future(); + } + + public ContextInternal context() { + return original.context(); + } + + @Override + public void addListener(Listener listener) { + original.addListener(listener); + } + + @Override + public void operationComplete(Future future) throws Exception { + original.operationComplete(future); + } + + @Override + public boolean isComplete() { + return original.isComplete(); + } + + @Override + public io.vertx.core.Future onComplete(Handler> handler) { + return original.onComplete(handler); + } + + @Override + public HttpClientRequest result() { + return original.result(); + } + + @Override + public Throwable cause() { + return original.cause(); + } + + @Override + public boolean succeeded() { + return original.succeeded(); + } + + @Override + public boolean failed() { + return original.failed(); + } + + @Override + public io.vertx.core.Future compose(Function> successMapper, + Function> failureMapper) { + return original.compose(successMapper, failureMapper); + } + + @Override + public io.vertx.core.Future eventually(Function> mapper) { + return original.eventually(mapper); + } + + @Override + public io.vertx.core.Future map(Function mapper) { + return original.map(mapper); + } + + @Override + public io.vertx.core.Future map(V value) { + return original.map(value); + } + + @Override + public io.vertx.core.Future otherwise(Function mapper) { + return original.otherwise(mapper); + } + + @Override + public io.vertx.core.Future otherwise(HttpClientRequest value) { + return original.otherwise(value); + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java index 2b57204455..0749efd318 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java @@ -18,7 +18,6 @@ public class PromiseHandlerWrapper implements Handler> { private Token token; public PromiseHandlerWrapper(Handler> original, Token token) { - System.out.println("wrapped constr " + token); this.original = original; this.token = token; } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java index e511bbcb17..771d7aebef 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java @@ -13,6 +13,7 @@ import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.Segment; import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Transaction; import com.newrelic.api.agent.weaver.Weaver; import io.vertx.core.Handler; import io.vertx.core.http.impl.HttpClientResponseImpl; @@ -35,7 +36,6 @@ private VertxCoreUtil() { public static void storeToken(Handler handler) { if (handler != null && AgentBridge.getAgent().getTransaction(false) != null) { - System.out.println("storeToken --- "); tokenMap.put(handler, NewRelic.getAgent().getTransaction().getToken()); } } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java new file mode 100644 index 0000000000..e5288565ac --- /dev/null +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java @@ -0,0 +1,31 @@ +package io.vertx.core.http.impl; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.HttpClientRequestPromiseWrapper; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.impl.future.PromiseInternal; +import io.vertx.core.net.SocketAddress; + +@Weave(type = MatchType.BaseClass, originalName = "io.vertx.core.http.impl.HttpClientImpl") +public class HttpClientImpl_Instrumentation { + private void request( + HttpMethod method, + SocketAddress peerAddress, + SocketAddress server, + String host, + int port, + Boolean useSSL, + String requestURI, + MultiMap headers, + long timeout, + Boolean followRedirects, + PromiseInternal requestPromise) { + requestPromise = new HttpClientRequestPromiseWrapper(requestPromise, NewRelic.getAgent().getTransaction().getToken()); + Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java index cf996bf7a4..a59e0a2c74 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -39,45 +39,35 @@ public abstract class HttpClientRequestBase_Instrumentation { @NewField public Segment segment; - @NewField - public Token token; - public abstract MultiMap headers(); - @Trace(async = true) - public HttpClientRequest response(Handler> handler) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response " + handler); - if (AgentBridge.getAgent().getTransaction(false) != null) { - this.token = NewRelic.getAgent().getTransaction().getToken(); - System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response"); - segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); - segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); - } - - return Weaver.callOriginal(); - } +// @Trace(async = true) +// public HttpClientRequest response(Handler> handler) { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response " + handler); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// this.token = NewRelic.getAgent().getTransaction().getToken(); +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// } +// +// return Weaver.callOriginal(); +// } @Trace(async = true) void handleResponse(Promise promise, HttpClientResponse resp, long timeoutMs) { AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse()"); - System.out.println("----------- linkAndExpire " + this.token.link() + " " + this.token); if (segment != null) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse() segment != null"); - System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); - Token segmentToken = segment.getTransaction().getToken(); - System.out.println("segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segmentToken); - reportExternal(resp, segment); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse() segment != null && this.token != null"); + final Token segmentToken = segment.getTransaction().getToken(); + reportExternal(resp, segment); segment.end(); - System.out.println("2222222222222 link " + segmentToken.link()); - System.out.println("33333 expire " + segmentToken.expire()); - } - this.token.expire(); - this.token = null; - Transaction t1 = AgentBridge.getAgent().getTransaction(); - Transaction t2 = NewRelic.getAgent().getTransaction(); + segmentToken.linkAndExpire(); + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } Weaver.callOriginal(); } @@ -91,6 +81,8 @@ void handleException(Throwable t) { final Token token = segment.getTransaction().getToken(); segment.end(); token.linkAndExpire(); + + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); } Weaver.callOriginal(); } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java index c0a9af31f7..f5ea66c890 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java @@ -25,50 +25,38 @@ @Weave(originalName = "io.vertx.core.http.impl.HttpClientRequestImpl") public abstract class HttpClientRequestImpl_Instrumentation extends HttpClientRequestBase_Instrumentation { -// public Future end(Buffer chunk) { -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk"); -// if (AgentBridge.getAgent().getTransaction(false) != null) { -// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk 2"); -// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); -// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); -// } -// return Weaver.callOriginal(); -// } -// -// public void end(Buffer chunk, Handler> handler) { -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler"); -// if (AgentBridge.getAgent().getTransaction(false) != null) { -// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler 2"); -// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); -// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); -// } -// Weaver.callOriginal(); -// } -// -// public Future end() { -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end()"); -// if (AgentBridge.getAgent().getTransaction(false) != null) { -// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end() 2"); -// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); -// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); -// System.out.println("end() Segment -- " + segment); -// } -// return Weaver.callOriginal(); -// } -// -// public void end(Handler> handler) { -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler)"); -// if (AgentBridge.getAgent().getTransaction(false) != null) { -// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler) 2"); -// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); -// System.out.println("!!!!segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segment.getTransaction().getToken()); -// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); -// System.out.println("end(Handler> Segment -- " + segment); -// } -// Weaver.callOriginal(); -// } + public Future end(Buffer chunk) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk"); + if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk 2"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + return Weaver.callOriginal(); + } + + public void end(Buffer chunk, Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler"); + if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler 2"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + Weaver.callOriginal(); + } + + public void end(Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler)"); + if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler) 2"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + System.out.println("!!!!segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segment.getTransaction().getToken()); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + System.out.println("end(Handler> Segment -- " + segment); + } + Weaver.callOriginal(); + } } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java index c5630d9a81..3570a9a149 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java @@ -6,6 +6,8 @@ */ package io.vertx.core.impl; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Transaction; import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -17,8 +19,10 @@ @Weave(originalName = "io.vertx.core.impl.AbstractContext") abstract class AbstractContext_Instrumentation { private static void setResultHandler(ContextInternal ctx, Future fut, Handler> resultHandler) { - System.out.println("setResulthandler ----"); - resultHandler = new AsyncHandlerWrapper<>(resultHandler, NewRelic.getAgent().getTransaction().getToken()); + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (txn != null) { + resultHandler = new AsyncHandlerWrapper<>(resultHandler, NewRelic.getAgent().getTransaction().getToken()); + } Weaver.callOriginal(); } } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java index 500a580c3d..32f0cff9ab 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java @@ -7,6 +7,8 @@ package io.vertx.core.impl; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Transaction; import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; @@ -18,8 +20,10 @@ @Weave(originalName = "io.vertx.core.impl.ContextImpl") abstract class ContextImpl_Instrumentation { static Future executeBlocking(ContextInternal context, Handler> blockingCodeHandler, WorkerPool workerPool, TaskQueue queue) { - System.out.println("executeBlocking ----"); - blockingCodeHandler = new PromiseHandlerWrapper(blockingCodeHandler, NewRelic.getAgent().getTransaction().getToken()); + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (txn != null) { + blockingCodeHandler = new PromiseHandlerWrapper(blockingCodeHandler, NewRelic.getAgent().getTransaction().getToken()); + } return Weaver.callOriginal(); } } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java index 251b351b99..c143d6ca2c 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java @@ -8,19 +8,13 @@ package io.vertx.core.impl.future; 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.vertx.instrumentation.VertxCoreUtil; -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Handler; @Weave(originalName = "io.vertx.core.impl.future.FutureImpl") abstract class FutureImpl_Instrumentation { - @NewField - private Handler handler = null; private Listener listener; public abstract boolean isComplete(); @@ -32,53 +26,18 @@ public void addListener(Listener listener) { } else { VertxCoreUtil.storeToken(listener); } -// if (!isComplete()) { -// System.out.println("addListener " + " " + this.listener + " " + listener + " " + System.identityHashCode(listener) + " " + System.currentTimeMillis()); -// assignAndStoreHandlerInstance(listener); -// } Weaver.callOriginal(); } - @Trace(async = true, excludeFromTransactionTrace = true) - public Future onSuccess(Handler handler) { - System.out.println("success"); - //assignAndStoreHandlerInstance(handler); - return Weaver.callOriginal(); - } - - @Trace(async = true, excludeFromTransactionTrace = true) - public Future onComplete(Handler handler) { - System.out.println("complete"); - //assignAndStoreHandlerInstance(handler); - return Weaver.callOriginal(); - } - - @Trace(async = true, excludeFromTransactionTrace = true) - public Future onFailure(Handler handler) { - //assignAndStoreHandlerInstance(handler); - return Weaver.callOriginal(); - } - @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryComplete(Object result) { - System.out.println("trycomplete " + " " + listener + " " + System.identityHashCode(this) + " " + System.currentTimeMillis()); VertxCoreUtil.linkAndExpireToken(this.listener); return Weaver.callOriginal(); } @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryFail(Throwable cause) { - System.out.println("tryFail"); VertxCoreUtil.linkAndExpireToken(this.listener); return Weaver.callOriginal(); } - - private void assignAndStoreHandlerInstance(Listener listener) { - VertxCoreUtil.storeToken(listener); - } - -// private void assignAndStoreHandlerInstance(Handler listener) { -// VertxCoreUtil.storeToken(listener); -// } - } diff --git a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java index 2aaa25e9d1..6b87f6b37e 100644 --- a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java +++ b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java @@ -19,11 +19,8 @@ import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.Trace; import io.vertx.core.Future; -import io.vertx.core.Handler; import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; import io.vertx.core.http.HttpMethod; @@ -78,34 +75,30 @@ public static void afterClass() { } @Test - public void testGet() throws InterruptedException { - getCall(); + public void testGet_withCallbacks() throws InterruptedException { + getCall_withCallbacks(); // Wait for transaction to finish - System.out.println("testGet " + NewRelic.getAgent().getTransaction()); InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbacks", "localhost"); } @Trace(dispatcher = true) - public void getCall() throws InterruptedException { - System.out.println("dispatcher -- " + NewRelic.getAgent().getTransaction()); + public void getCall_withCallbacks() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - //httpClient.close(); + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { if (reqAsyncResult.succeeded()) { //Request object successfully created HttpClientRequest request = reqAsyncResult.result(); - request.send(respAsyncResult -> { //Sending the request, waiting for response in ar2 + request.send(respAsyncResult -> { //Sending the request if (respAsyncResult.succeeded()) { - System.out.println("respAsyncResult succeed"); + NewRelic.addCustomParameter("responseHandler", "true"); HttpClientResponse response = respAsyncResult.result(); - int statusCode = response.statusCode(); latch.countDown(); - System.out.println("response: " + response + " " + statusCode); - response.body(respBufferAsyncResult -> { //Retriev response + response.body(respBufferAsyncResult -> { //Retrieve response if (respBufferAsyncResult.succeeded()) { - Buffer body = respBufferAsyncResult.result(); - // Handle body entirely + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body } else { // Handle server error, for example, connection closed } @@ -119,23 +112,34 @@ public void getCall() throws InterruptedException { } }); latch.await(); - - System.out.println("testGet " + NewRelic.getAgent().getTransaction()); } @Test - public void testGetNow() throws InterruptedException { - getNowCall(); + public void testGet_withCallbackAndFutures() throws InterruptedException { + getCall_withCallbackAndFutures(); // Wait for transaction to finish - InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getNowCall", "localhost"); + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbackAndFutures", "localhost"); } @Trace(dispatcher = true) - private void getNowCall() throws InterruptedException { + public void getCall_withCallbackAndFutures() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - //httpClient.getNow(port, "localhost", "/", requestHandler(latch)); + + httpClient.request(HttpMethod.GET, port, "localhost", "/", ar -> { + if (ar.succeeded()) { + HttpClientRequest request = ar.result(); + request.send("foo") + .onSuccess(response -> { + NewRelic.addCustomParameter("responseHandler", "true"); + NewRelic.addCustomParameter("bodyHandler", "true"); + latch.countDown(); + }).onFailure(err -> { + // + }); + } + }); latch.await(); } @@ -151,60 +155,25 @@ public void testPost() throws InterruptedException { private void postCall() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - //httpClient.post(port, "localhost", "/", requestHandler(latch)).end(); - latch.await(); - } - - @Test - public void testEndMethods() throws InterruptedException { - endMethods(); - assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); - final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( - "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/endMethods").iterator().next(); - assertEquals(4, externalRequest.getCount()); - assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); - assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/allOther")); - assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/all")); - } - - @Trace(dispatcher = true) - public void endMethods() throws InterruptedException { - HttpClient httpClient = vertx.createHttpClient(); - CountDownLatch latch = new CountDownLatch(4); - - Buffer bufferChunk = Buffer.buffer("buffer chunk!"); - String stringChunk = "string chunk!"; - String encoding = "UTF-8"; - - // tests the various overloaded versions of the end method - //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); - //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(bufferChunk); - //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk, encoding); - //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk); - latch.await(); - } - - @Test - public void testMethods() throws InterruptedException { - requestMethods(); - assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); - final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( - "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/requestMethods").iterator().next(); - assertEquals(5, externalRequest.getCount()); - assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); - assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/allOther")); - assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/all")); - } - @Trace(dispatcher = true) - public void requestMethods() throws InterruptedException { - HttpClient httpClient = vertx.createHttpClient(); - CountDownLatch latch = new CountDownLatch(5); - //httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); - //httpClient.request(HttpMethod.POST, port, "localhost", "/hi", requestHandler(latch)).end(); - //httpClient.request(HttpMethod.PUT, port, "localhost", "/hi", requestHandler(latch)).end(); - //httpClient.request(HttpMethod.HEAD, port, "localhost", "/hi", requestHandler(latch)).end(); - //httpClient.request(HttpMethod.DELETE, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.POST, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); latch.await(); } @@ -218,13 +187,27 @@ public void testRedirect() throws InterruptedException { @Trace(dispatcher = true) public void redirect() throws InterruptedException { - HttpClient httpClient = vertx.createHttpClient(); CountDownLatch latch = new CountDownLatch(1); - //httpClient.get(port, "localhost", "/redirect") - // .putHeader("statusCode", "301") - // .setFollowRedirects(true) - // .handler(requestHandler(latch)) - // .end(); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.putHeader("statusCode", "301"); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); latch.await(); } @@ -278,10 +261,25 @@ public void cat(HttpTestServer httpServer) throws Exception { try { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - //httpClient.get(httpServer.getEndPoint().getPort(), httpServer.getEndPoint().getHost(), "/") - //.putHeader(HttpTestServer.DO_CAT, "true") - // .handler(requestHandler(latch)) - // .end(); + httpClient.request(HttpMethod.GET, httpServer.getEndPoint().getPort(),httpServer.getEndPoint().getHost(), "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.putHeader(HttpTestServer.DO_CAT, "true"); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); latch.await(); } finally { httpServer.shutdown(); @@ -289,17 +287,53 @@ public void cat(HttpTestServer httpServer) throws Exception { } @Test - public void testUnknownHost() throws Exception { - unknownHost(); + public void testUnknownHost_withCallbacks() throws Exception { + unknownHost_withCallbacks(); + assertUnknownHostExternal(); + } + + @Trace(dispatcher = true) + private void unknownHost_withCallbacks() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/", ar -> { + if (ar.failed()) { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + } + }); + latch.await(); + } + + @Test + public void testUnknownHost_withReturnedFuture() throws Exception { + unknownHost_withReturnedFuture(); + assertUnknownHostExternal(); + } + + @Trace(dispatcher = true) + private void unknownHost_withReturnedFuture() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + + Future r = httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/"); + r.onFailure(err -> { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + }); + latch.await(); + } + + private void assertUnknownHostExternal() { Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(250)); final String txn = introspector.getTransactionNames().iterator().next(); assertNotNull("Transaction not found", txn); - assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/end")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/handleResponse")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/handleResponse")); // Unknown hosts generate no external rollups assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/allOther")); @@ -310,47 +344,19 @@ public void testUnknownHost() throws Exception { assertTrue(event.getAttributes().containsKey("exceptionHandler")); } - @Trace(dispatcher = true) - private void unknownHost() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - HttpClient httpClient = vertx.createHttpClient(); - //httpClient.get(port, "notARealHostDuderina.com", "/") - // .exceptionHandler(exceptionHandler(latch)) - // .handler(requestHandler(null)) - // .end(); - latch.await(); - } - - private Handler exceptionHandler(CountDownLatch latch) { - return error -> { - NewRelic.addCustomParameter("exceptionHandler", "true"); - latch.countDown(); - }; - } - - private Handler requestHandler(CountDownLatch latch) { - return response -> { - NewRelic.addCustomParameter("responseHandler", "true"); - response.bodyHandler(body -> { - NewRelic.addCustomParameter("bodyHandler", "true"); - latch.countDown(); - }); - }; - } - - public void assertExternal(String transactionName, String host) { + private void assertExternal(String transactionName, String host) { Introspector introspector = InstrumentationTestRunner.getIntrospector(); Collection externalRequests = introspector.getExternalRequests(transactionName); ExternalRequest request = externalRequests.iterator().next(); assertEquals(host, request.getHostname()); assertEquals("Vertx-Client", request.getLibrary()); - assertEquals("end", request.getOperation()); + assertEquals("handleResponse", request.getOperation()); Collection events = introspector.getTransactionEvents(transactionName); TransactionEvent event = events.iterator().next(); assertTrue(event.getAttributes().containsKey("responseHandler")); - assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/end")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/handleResponse")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/handleResponse")); Collection transactionEvents = introspector.getTransactionEvents(transactionName); assertEquals(1, transactionEvents.size()); From ca0802e00b54e1485041275fbb301a023e8193cc Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Fri, 3 Nov 2023 07:31:54 -0400 Subject: [PATCH 08/16] v4.3.2 module commit --- instrumentation/vertx-core-4.3.2/build.gradle | 2 +- .../instrumentation/AsyncHandlerWrapper.java | 35 +++ .../HttpClientRequestPromiseWrapper.java | 158 ++++++++++ .../vertx/instrumentation/InboundWrapper.java | 2 +- .../instrumentation/OutboundWrapper.java | 2 +- .../PromiseHandlerWrapper.java | 36 +++ .../vertx/instrumentation/VertxCoreUtil.java | 19 +- .../impl/HttpClientImpl_Instrumentation.java | 22 ++ ...HttpClientRequestBase_Instrumentation.java | 37 ++- ...HttpClientRequestImpl_Instrumentation.java | 32 +- .../impl/ContextBase_Instrumentation.java | 47 +-- .../FutureImpl_Instrumentation.java | 23 +- .../instrumentation/VertxBlockingTest.java | 5 +- .../nr/vertx/instrumentation/VertxClient.java | 275 ++++++++++-------- .../nr/vertx/instrumentation/VertxFuture.java | 163 ++++++++--- .../java/com/newrelic/agent/TokenImpl.java | 1 - .../java/com/newrelic/agent/Transaction.java | 1 - 17 files changed, 631 insertions(+), 229 deletions(-) create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java create mode 100644 instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java rename instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/{ => future}/FutureImpl_Instrumentation.java (57%) diff --git a/instrumentation/vertx-core-4.3.2/build.gradle b/instrumentation/vertx-core-4.3.2/build.gradle index d3bd7ec4a5..b520cba843 100644 --- a/instrumentation/vertx-core-4.3.2/build.gradle +++ b/instrumentation/vertx-core-4.3.2/build.gradle @@ -9,7 +9,7 @@ jar { dependencies { implementation(project(":agent-bridge")) implementation("io.vertx:vertx-core:4.3.2") - testImplementation("io.vertx:vertx-core:4.0.0") + testImplementation("io.vertx:vertx-core:4.3.2") } verifyInstrumentation { diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java new file mode 100644 index 0000000000..00f88bfb4d --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java @@ -0,0 +1,35 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; + +public class AsyncHandlerWrapper implements Handler> { + private Handler> original; + + private Token token; + + public AsyncHandlerWrapper(Handler> original, Token token) { + this.original = original; + this.token = token; + } + + @Override + @Trace(async = true, excludeFromTransactionTrace = true) + public void handle(AsyncResult event) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + this.original.handle(event); + this.original = null; + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java new file mode 100644 index 0000000000..4c577f7fe0 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java @@ -0,0 +1,158 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.Transaction; +import io.netty.util.concurrent.Future; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.impl.ContextInternal; +import io.vertx.core.impl.future.Listener; +import io.vertx.core.impl.future.PromiseInternal; + +import java.util.function.Function; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +public class HttpClientRequestPromiseWrapper implements PromiseInternal { + + private final PromiseInternal original; + + private Token token; + + public HttpClientRequestPromiseWrapper(PromiseInternal original, Token token) { + this.original = original; + this.token = token; + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public void complete(HttpClientRequest req) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + original.complete(req); + } + + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryFail(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (t.toString().contains("UnknownHostException") && txn != null) { + Segment segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + VertxCoreUtil.reportUnknownHost(segment); + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } + + return original.tryFail(t); + } + + @Override + public boolean tryComplete(HttpClientRequest result) { + return original.tryComplete(result); + } + + @Override + public io.vertx.core.Future future() { + return original.future(); + } + + public ContextInternal context() { + return original.context(); + } + + @Override + public void addListener(Listener listener) { + original.addListener(listener); + } + + @Override + public void operationComplete(Future future) throws Exception { + original.operationComplete(future); + } + + @Override + public boolean isComplete() { + return original.isComplete(); + } + + @Override + public io.vertx.core.Future onComplete(Handler> handler) { + return original.onComplete(handler); + } + + @Override + public HttpClientRequest result() { + return original.result(); + } + + @Override + public Throwable cause() { + return original.cause(); + } + + @Override + public boolean succeeded() { + return original.succeeded(); + } + + @Override + public boolean failed() { + return original.failed(); + } + + @Override + public io.vertx.core.Future compose(Function> successMapper, + Function> failureMapper) { + return original.compose(successMapper, failureMapper); + } + + @Override + public io.vertx.core.Future transform(Function, io.vertx.core.Future> mapper) { + return original.transform(mapper); + } + + @Override + public io.vertx.core.Future eventually(Function> mapper) { + return original.eventually(mapper); + } + + @Override + public io.vertx.core.Future map(Function mapper) { + return original.map(mapper); + } + + @Override + public io.vertx.core.Future map(V value) { + return original.map(value); + } + + @Override + public io.vertx.core.Future otherwise(Function mapper) { + return original.otherwise(mapper); + } + + @Override + public io.vertx.core.Future otherwise(HttpClientRequest value) { + return original.otherwise(value); + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java index e7289a87b0..41531affba 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java index 0bc00583ca..7f4765a262 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java new file mode 100644 index 0000000000..0749efd318 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java @@ -0,0 +1,36 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Handler; +import io.vertx.core.Promise; + +public class PromiseHandlerWrapper implements Handler> { + + private Handler> original; + + private Token token; + + public PromiseHandlerWrapper(Handler> original, Token token) { + this.original = original; + this.token = token; + } + + @Override + @Trace(async = true, excludeFromTransactionTrace = true) + public void handle(Promise event) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + this.original.handle(event); + this.original = null; + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java index b48dd349ae..f85fdb7ebd 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ @@ -16,6 +16,7 @@ import com.newrelic.api.agent.weaver.Weaver; import io.vertx.core.Handler; import io.vertx.core.http.impl.HttpClientResponseImpl; +import io.vertx.core.impl.future.Listener; import java.net.URI; import java.net.URISyntaxException; @@ -29,19 +30,19 @@ private VertxCoreUtil() { private static final Map tokenMap = AgentBridge.collectionFactory.createConcurrentWeakKeyedMap(); public static final String VERTX_CLIENT = "Vertx-Client"; - public static final String END = "end"; + public static final String END = "handleResponse"; - private static URI UNKNOWN_HOST_URI = URI.create("http://UnknownHost/"); + private static final URI UNKNOWN_HOST_URI = URI.create("http://UnknownHost/"); - public static void storeToken(Handler handler) { - if (handler != null && AgentBridge.getAgent().getTransaction(false) != null) { - tokenMap.put(handler, NewRelic.getAgent().getTransaction().getToken()); + public static void storeToken(Listener listener) { + if (listener != null && AgentBridge.getAgent().getTransaction(false) != null) { + tokenMap.put(listener, NewRelic.getAgent().getTransaction().getToken()); } } - public static void linkAndExpireToken(Handler handler) { - if (handler != null) { - final Token token = tokenMap.remove(handler); + public static void linkAndExpireToken(Listener listener) { + if (listener != null) { + final Token token = tokenMap.remove(listener); if (token != null) { token.linkAndExpire(); } diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java new file mode 100644 index 0000000000..efed450c92 --- /dev/null +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java @@ -0,0 +1,22 @@ +package io.vertx.core.http.impl; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.HttpClientRequestPromiseWrapper; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.RequestOptions; +import io.vertx.core.impl.future.PromiseInternal; +import io.vertx.core.net.SocketAddress; + +@Weave(type = MatchType.BaseClass, originalName = "io.vertx.core.http.impl.HttpClientImpl") +public class HttpClientImpl_Instrumentation { + + private void doRequest(RequestOptions request, PromiseInternal promise) { + promise = new HttpClientRequestPromiseWrapper(promise, NewRelic.getAgent().getTransaction().getToken()); + Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java index 3cb00268f0..76e3c8225e 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -1,25 +1,29 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ package io.vertx.core.http.impl; +import com.newrelic.agent.bridge.AgentBridge; import com.newrelic.api.agent.Segment; 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.vertx.instrumentation.VertxCoreUtil; import io.vertx.core.MultiMap; +import io.vertx.core.Promise; import io.vertx.core.http.HttpClientResponse; import java.net.UnknownHostException; +import java.util.logging.Level; -@Weave(originalName = "io.vertx.core.http.impl.HttpClientRequestBase") +@Weave(type = MatchType.BaseClass, originalName = "io.vertx.core.http.impl.HttpClientRequestBase") public abstract class HttpClientRequestBase_Instrumentation { @NewField @@ -27,20 +31,39 @@ public abstract class HttpClientRequestBase_Instrumentation { public abstract MultiMap headers(); +// @Trace(async = true) +// public HttpClientRequest response(Handler> handler) { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response " + handler); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// this.token = NewRelic.getAgent().getTransaction().getToken(); +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// } +// +// return Weaver.callOriginal(); +// } + @Trace(async = true) - void handleResponse(HttpClientResponse resp) { + void handleResponse(Promise promise, HttpClientResponse resp, long timeoutMs) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse()"); if (segment != null) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse() segment != null && this.token != null"); + + final Token segmentToken = segment.getTransaction().getToken(); reportExternal(resp, segment); - final Token token = segment.getTransaction().getToken(); segment.end(); - token.linkAndExpire(); - } + segmentToken.linkAndExpire(); + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } Weaver.callOriginal(); } @Trace(async = true) void handleException(Throwable t) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleException()"); if (segment != null) { if (t instanceof UnknownHostException) { VertxCoreUtil.reportUnknownHost(segment); @@ -48,6 +71,8 @@ void handleException(Throwable t) { final Token token = segment.getTransaction().getToken(); segment.end(); token.linkAndExpire(); + + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); } Weaver.callOriginal(); } diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java index 54b6612f56..f5ea66c890 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ @@ -17,6 +17,8 @@ import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; +import java.util.logging.Level; + import static com.nr.vertx.instrumentation.VertxCoreUtil.END; import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; @@ -24,41 +26,37 @@ public abstract class HttpClientRequestImpl_Instrumentation extends HttpClientRequestBase_Instrumentation { public Future end(Buffer chunk) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk"); if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk 2"); segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); } - Weaver.callOriginal(); - - //hack - return null; + return Weaver.callOriginal(); } public void end(Buffer chunk, Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler"); if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler 2"); segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); } Weaver.callOriginal(); } - public Future end() { - if (AgentBridge.getAgent().getTransaction(false) != null) { - segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); - segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); - } - Weaver.callOriginal(); - - //hack - return null; - } - public void end(Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler)"); if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler) 2"); segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + System.out.println("!!!!segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segment.getTransaction().getToken()); segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + System.out.println("end(Handler> Segment -- " + segment); } Weaver.callOriginal(); } - } diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java index f896ac0976..606b08e9eb 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/ContextBase_Instrumentation.java @@ -1,36 +1,39 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ package io.vertx.core.impl; +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Transaction; +import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.AsyncHandlerWrapper; +import com.nr.vertx.instrumentation.PromiseHandlerWrapper; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.Promise; @Weave(originalName = "io.vertx.core.impl.ContextBase") public abstract class ContextBase_Instrumentation { + static Future executeBlocking(ContextInternal context, Handler> blockingCodeHandler, WorkerPool workerPool, TaskQueue queue) { + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (txn != null) { + blockingCodeHandler = new PromiseHandlerWrapper(blockingCodeHandler, NewRelic.getAgent().getTransaction().getToken()); + } + return Weaver.callOriginal(); + } - //static Future executeBlocking(ContextInternal context, Handler> blockingCodeHandler, WorkerPool workerPool, TaskQueue queue) - -// void executeBlocking(Handler> blockingCodeHandler, Handler resultHandler, -// Executor exec, TaskQueue queue, PoolMetrics metrics) { -// VertxCoreUtil.storeToken(blockingCodeHandler); -// VertxCoreUtil.storeToken(resultHandler); -// Weaver.callOriginal(); -// } -// -// @Trace(async = true) -// private void lambda$executeBlocking$2(PoolMetrics metricsHandler, Object object, Handler handler, Handler resultHandler) { -// VertxCoreUtil.linkAndExpireToken(handler); -// Weaver.callOriginal(); -// } -// -// @Trace(async = true) -// private static void lambda$null$0(Handler handler, AsyncResult result, Void v) { -// VertxCoreUtil.linkAndExpireToken(handler); -// Weaver.callOriginal(); -// } - + static void setResultHandler(ContextInternal ctx, Future fut, Handler> resultHandler) { + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (txn != null) { + resultHandler = new AsyncHandlerWrapper<>(resultHandler, NewRelic.getAgent().getTransaction().getToken()); + } + Weaver.callOriginal(); + } } diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java similarity index 57% rename from instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java rename to instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java index 9266fcee33..c143d6ca2c 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/FutureImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java @@ -1,48 +1,43 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ -package io.vertx.core.impl; +package io.vertx.core.impl.future; import com.newrelic.api.agent.Trace; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.vertx.instrumentation.VertxCoreUtil; -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Handler; @Weave(originalName = "io.vertx.core.impl.future.FutureImpl") abstract class FutureImpl_Instrumentation { - //private Handler handler = Weaver.callOriginal(); + private Listener listener; - //to use concrete method in instrumented class public abstract boolean isComplete(); - @Trace(async = true, excludeFromTransactionTrace = true) - public Future onComplete(Handler handler) { + public void addListener(Listener listener) { if (isComplete()) { - VertxCoreUtil.linkAndExpireToken(handler); + VertxCoreUtil.linkAndExpireToken(listener); } else { - VertxCoreUtil.storeToken(handler); + VertxCoreUtil.storeToken(listener); } - return Weaver.callOriginal(); + Weaver.callOriginal(); } @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryComplete(Object result) { - //VertxCoreUtil.linkAndExpireToken(handler); + VertxCoreUtil.linkAndExpireToken(this.listener); return Weaver.callOriginal(); } @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryFail(Throwable cause) { - //VertxCoreUtil.linkAndExpireToken(handler); + VertxCoreUtil.linkAndExpireToken(this.listener); return Weaver.callOriginal(); } } diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java index 622d18e349..0fa7ac126a 100644 --- a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ @@ -28,7 +28,7 @@ public class VertxBlockingTest { @Test - public void testBlocking() throws InterruptedException { + public void executeBlocking_withPromiseAndResult() throws InterruptedException { Vertx vertx = Vertx.vertx(); try { executeBlocking(vertx); @@ -56,5 +56,4 @@ void executeBlocking(Vertx vertx) throws InterruptedException { }); countDownLatch.await(); } - } diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java index 9e48e5905c..6b87f6b37e 100644 --- a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java @@ -18,9 +18,8 @@ import com.newrelic.agent.introspec.internal.HttpServerLocator; import com.newrelic.api.agent.NewRelic; import com.newrelic.api.agent.Trace; -import io.vertx.core.Handler; +import io.vertx.core.Future; import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; @@ -44,20 +43,22 @@ @InstrumentationTestConfig(includePrefixes = { "io.vertx" }) public class VertxClient { -/* private static int port; - private static HttpServer server; + private static Future server; private static Vertx vertx; @BeforeClass public static void beforeClass() { port = getAvailablePort(); vertx = Vertx.vertx(); + server = vertx.createHttpServer().requestHandler(request -> { final String statusCode = request.getHeader("statusCode"); if (statusCode == null) { + System.out.println("statusCode is null"); request.response().end("response"); } else { + System.out.println("statusCode is NOT null -- " + statusCode); if (request.absoluteURI().equals("/redirect")) { request.headers().clear(); request.response().putHeader("Location", "http://localhost:" + port + "/other"); @@ -69,40 +70,76 @@ public static void beforeClass() { @AfterClass public static void afterClass() { - server.close(); + server.result().close(); vertx.close(); } @Test - public void testGet() throws InterruptedException { - getCall(); + public void testGet_withCallbacks() throws InterruptedException { + getCall_withCallbacks(); // Wait for transaction to finish - InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall", "localhost"); + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbacks", "localhost"); } @Trace(dispatcher = true) - public void getCall() throws InterruptedException { + public void getCall_withCallbacks() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - HttpClientRequest request = httpClient.options(port, "localhost", "/").handler(requestHandler(latch)); - request.end(); + + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } else { + // Handle server error, for example, connection closed + } + }); + } else { + // Handle server error, for example, connection closed + } + }); + } else { + // Connection error, for example, invalid server or invalid SSL certificate + } + }); latch.await(); } @Test - public void testGetNow() throws InterruptedException { - getNowCall(); + public void testGet_withCallbackAndFutures() throws InterruptedException { + getCall_withCallbackAndFutures(); // Wait for transaction to finish - InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getNowCall", "localhost"); + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbackAndFutures", "localhost"); } @Trace(dispatcher = true) - private void getNowCall() throws InterruptedException { + public void getCall_withCallbackAndFutures() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - httpClient.getNow(port, "localhost", "/", requestHandler(latch)); + + httpClient.request(HttpMethod.GET, port, "localhost", "/", ar -> { + if (ar.succeeded()) { + HttpClientRequest request = ar.result(); + request.send("foo") + .onSuccess(response -> { + NewRelic.addCustomParameter("responseHandler", "true"); + NewRelic.addCustomParameter("bodyHandler", "true"); + latch.countDown(); + }).onFailure(err -> { + // + }); + } + }); latch.await(); } @@ -118,60 +155,25 @@ public void testPost() throws InterruptedException { private void postCall() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - httpClient.post(port, "localhost", "/", requestHandler(latch)).end(); - latch.await(); - } - - @Test - public void testEndMethods() throws InterruptedException { - endMethods(); - assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); - final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( - "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/endMethods").iterator().next(); - assertEquals(4, externalRequest.getCount()); - assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); - assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/allOther")); - assertEquals(4, MetricsHelper.getUnscopedMetricCount("External/all")); - } - - @Trace(dispatcher = true) - public void endMethods() throws InterruptedException { - HttpClient httpClient = vertx.createHttpClient(); - CountDownLatch latch = new CountDownLatch(4); - - Buffer bufferChunk = Buffer.buffer("buffer chunk!"); - String stringChunk = "string chunk!"; - String encoding = "UTF-8"; - // tests the various overloaded versions of the end method - httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); - httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(bufferChunk); - httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk, encoding); - httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(stringChunk); - latch.await(); - } - - @Test - public void testMethods() throws InterruptedException { - requestMethods(); - assertEquals(1, InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000)); - final ExternalRequest externalRequest = InstrumentationTestRunner.getIntrospector().getExternalRequests( - "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/requestMethods").iterator().next(); - assertEquals(5, externalRequest.getCount()); - assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); - assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/allOther")); - assertEquals(5, MetricsHelper.getUnscopedMetricCount("External/all")); - } - - @Trace(dispatcher = true) - public void requestMethods() throws InterruptedException { - HttpClient httpClient = vertx.createHttpClient(); - CountDownLatch latch = new CountDownLatch(5); - httpClient.request(HttpMethod.GET, port, "localhost", "/hi", requestHandler(latch)).end(); - httpClient.request(HttpMethod.POST, port, "localhost", "/hi", requestHandler(latch)).end(); - httpClient.request(HttpMethod.PUT, port, "localhost", "/hi", requestHandler(latch)).end(); - httpClient.request(HttpMethod.HEAD, port, "localhost", "/hi", requestHandler(latch)).end(); - httpClient.request(HttpMethod.DELETE, port, "localhost", "/hi", requestHandler(latch)).end(); + httpClient.request(HttpMethod.POST, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); latch.await(); } @@ -185,13 +187,27 @@ public void testRedirect() throws InterruptedException { @Trace(dispatcher = true) public void redirect() throws InterruptedException { - HttpClient httpClient = vertx.createHttpClient(); CountDownLatch latch = new CountDownLatch(1); - httpClient.get(port, "localhost", "/redirect") - .putHeader("statusCode", "301") - .setFollowRedirects(true) - .handler(requestHandler(latch)) - .end(); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.putHeader("statusCode", "301"); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); latch.await(); } @@ -245,10 +261,25 @@ public void cat(HttpTestServer httpServer) throws Exception { try { CountDownLatch latch = new CountDownLatch(1); HttpClient httpClient = vertx.createHttpClient(); - httpClient.get(httpServer.getEndPoint().getPort(), httpServer.getEndPoint().getHost(), "/") - .putHeader(HttpTestServer.DO_CAT, "true") - .handler(requestHandler(latch)) - .end(); + httpClient.request(HttpMethod.GET, httpServer.getEndPoint().getPort(),httpServer.getEndPoint().getHost(), "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.putHeader(HttpTestServer.DO_CAT, "true"); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); latch.await(); } finally { httpServer.shutdown(); @@ -256,17 +287,53 @@ public void cat(HttpTestServer httpServer) throws Exception { } @Test - public void testUnknownHost() throws Exception { - unknownHost(); + public void testUnknownHost_withCallbacks() throws Exception { + unknownHost_withCallbacks(); + assertUnknownHostExternal(); + } + @Trace(dispatcher = true) + private void unknownHost_withCallbacks() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/", ar -> { + if (ar.failed()) { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + } + }); + latch.await(); + } + + @Test + public void testUnknownHost_withReturnedFuture() throws Exception { + unknownHost_withReturnedFuture(); + assertUnknownHostExternal(); + } + + @Trace(dispatcher = true) + private void unknownHost_withReturnedFuture() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + + Future r = httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/"); + r.onFailure(err -> { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + }); + + latch.await(); + } + + private void assertUnknownHostExternal() { Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(250)); final String txn = introspector.getTransactionNames().iterator().next(); assertNotNull("Transaction not found", txn); - assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/end")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/handleResponse")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/handleResponse")); // Unknown hosts generate no external rollups assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/allOther")); @@ -277,47 +344,19 @@ public void testUnknownHost() throws Exception { assertTrue(event.getAttributes().containsKey("exceptionHandler")); } - @Trace(dispatcher = true) - private void unknownHost() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - HttpClient httpClient = vertx.createHttpClient(); - httpClient.get(port, "notARealHostDuderina.com", "/") - .exceptionHandler(exceptionHandler(latch)) - .handler(requestHandler(null)) - .end(); - latch.await(); - } - - private Handler exceptionHandler(CountDownLatch latch) { - return error -> { - NewRelic.addCustomParameter("exceptionHandler", "true"); - latch.countDown(); - }; - } - - private Handler requestHandler(CountDownLatch latch) { - return response -> { - NewRelic.addCustomParameter("responseHandler", "true"); - response.bodyHandler(body -> { - NewRelic.addCustomParameter("bodyHandler", "true"); - latch.countDown(); - }); - }; - } - - public void assertExternal(String transactionName, String host) { + private void assertExternal(String transactionName, String host) { Introspector introspector = InstrumentationTestRunner.getIntrospector(); Collection externalRequests = introspector.getExternalRequests(transactionName); ExternalRequest request = externalRequests.iterator().next(); assertEquals(host, request.getHostname()); assertEquals("Vertx-Client", request.getLibrary()); - assertEquals("end", request.getOperation()); + assertEquals("handleResponse", request.getOperation()); Collection events = introspector.getTransactionEvents(transactionName); TransactionEvent event = events.iterator().next(); assertTrue(event.getAttributes().containsKey("responseHandler")); - assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/end")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/end")); + assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/handleResponse")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/handleResponse")); Collection transactionEvents = introspector.getTransactionEvents(transactionName); assertEquals(1, transactionEvents.size()); @@ -343,6 +382,4 @@ private static int getAvailablePort() { } return port; } -*/ - } diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java index 831f9e01d1..f3676b8fd0 100644 --- a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java @@ -15,13 +15,13 @@ import com.newrelic.api.agent.Trace; import io.vertx.core.CompositeFuture; import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Map; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -32,74 +32,169 @@ public class VertxFuture { @Test public void testCompositeFuture() throws InterruptedException { - compositeFutures(); + compositeFuturesAllFuturesSucceed(); Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFutures"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFuturesAllFuturesSucceed"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); - assertTrue(attributes.containsKey("success")); + assertTrue(attributes.containsKey("compositeFuture")); } @Trace(dispatcher = true) - private void compositeFutures() throws InterruptedException { - final Future abc = Future.future(); - final Future def = Future.future(); - - final ExecutorService service = Executors.newSingleThreadExecutor(); - + private void compositeFuturesAllFuturesSucceed() throws InterruptedException { + Vertx vertx = Vertx.vertx(); CountDownLatch latch = new CountDownLatch(1); - CompositeFuture.all(abc, def).setHandler(result -> { - if (result.succeeded()) { - NewRelic.addCustomParameter("success", "yes"); + Promise promise1 = Promise.promise(); + Promise promise2 = Promise.promise(); + + CompositeFuture.all(promise1.future(), promise2.future()).onComplete((ar -> { + if (ar.succeeded()) { + NewRelic.addCustomParameter("compositeFuture", "yes"); } latch.countDown(); - }); + })); - service.submit(() -> { - abc.complete("abc"); - }); - - service.submit(() -> { - def.complete("def"); + vertx.setTimer(1, handler -> { + promise1.complete("promise1"); + promise2.complete("promise2"); }); latch.await(); } @Test - public void testFutureFail() throws InterruptedException { - failFuture(); + public void whenFutureFails_withThrowable_txnStillCompletes() throws InterruptedException { + failFutureWithThrowable(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithThrowable"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureFails_withString_txnStillCompletes() throws InterruptedException { + failFutureWithString(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithString"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureCompletes_txnStillCompletes() throws InterruptedException { + completeFutureSuccessfully(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfully"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureCompletes_withOnCompleteRegistered_txnStillCompletes() throws InterruptedException { + completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback(); Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFuture"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); } @Trace(dispatcher = true) - private void failFuture() throws InterruptedException { + private void failFutureWithString() throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(1); - Future future = Future.future(); - future.setHandler(ar -> { - if (ar.failed()) { - NewRelic.addCustomParameter("future", "failed"); - countDownLatch.countDown(); - } + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onFailure(ar -> { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.fail("oops"); + }); + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void failFutureWithThrowable() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onFailure(ar -> { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); }); - ExecutorService service = Executors.newSingleThreadExecutor(); - service.submit(() -> { - future.fail(new RuntimeException()); + vertx.setTimer(1, handler -> { + promise.fail(new IllegalArgumentException("foo")); }); countDownLatch.await(); } + @Trace(dispatcher = true) + private void completeFutureSuccessfully() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onSuccess(ar -> { + NewRelic.addCustomParameter("future", "success"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.complete("hooray"); + }); + + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onComplete(ar -> { + NewRelic.addCustomParameter("future", "onComplete"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.complete("hooray"); + }); + + countDownLatch.await(); + } } diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java index 1fe9616a7e..7aabd7a552 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/TokenImpl.java @@ -58,7 +58,6 @@ public Tracer getInitiatingTracer() { @Override public boolean expire() { - System.out.println("expire --- " + this); if (active.compareAndSet(Boolean.TRUE, Boolean.FALSE)) { Transaction tx = getTransaction().getTransactionIfExists(); if (tx != null) { diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java b/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java index ff250212a2..1b6af54e83 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java @@ -2209,7 +2209,6 @@ public Token getToken() { getMetricAggregator().incrementCounter(AgentBridge.currentApiSource.get().getSupportabilityMetric( MetricNames.SUPPORTABILITY_API_TOKEN)); - System.out.println("getToken --- " + token); return token; } From 063828e279a344bebbdcdc176e2152a51ae371bf Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Fri, 3 Nov 2023 09:21:38 -0400 Subject: [PATCH 09/16] correct verifyInstr libraries for earlier vertx modules --- instrumentation/vertx-core-3.3.0/build.gradle | 2 +- instrumentation/vertx-core-3.3.3/build.gradle | 2 +- instrumentation/vertx-core-3.4.1/build.gradle | 2 +- instrumentation/vertx-core-3.6.0/build.gradle | 2 +- instrumentation/vertx-core-3.8.0/build.gradle | 2 +- instrumentation/vertx-core-3.9.0/build.gradle | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/instrumentation/vertx-core-3.3.0/build.gradle b/instrumentation/vertx-core-3.3.0/build.gradle index ce4a2e627e..98b6f7c13a 100644 --- a/instrumentation/vertx-core-3.3.0/build.gradle +++ b/instrumentation/vertx-core-3.3.0/build.gradle @@ -12,7 +12,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-web:[3.3.0.CR1,3.3.3)' + passesOnly 'io.vertx:vertx-core:[3.3.0.CR1,3.3.3)' } site { diff --git a/instrumentation/vertx-core-3.3.3/build.gradle b/instrumentation/vertx-core-3.3.3/build.gradle index 37a85ff04d..c557a288b6 100644 --- a/instrumentation/vertx-core-3.3.3/build.gradle +++ b/instrumentation/vertx-core-3.3.3/build.gradle @@ -10,7 +10,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-web:[3.3.3,3.4.0]' + passesOnly 'io.vertx:vertx-core:[3.3.3,3.4.0]' } test { diff --git a/instrumentation/vertx-core-3.4.1/build.gradle b/instrumentation/vertx-core-3.4.1/build.gradle index 6de5290d69..b2b2e80de6 100644 --- a/instrumentation/vertx-core-3.4.1/build.gradle +++ b/instrumentation/vertx-core-3.4.1/build.gradle @@ -13,7 +13,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-web:[3.4.1,3.6.0)' + passesOnly 'io.vertx:vertx-core:[3.4.1,3.6.0)' excludeRegex '.*CR[0-9]*' } diff --git a/instrumentation/vertx-core-3.6.0/build.gradle b/instrumentation/vertx-core-3.6.0/build.gradle index 8ec0f53cc8..4237e1bcfa 100644 --- a/instrumentation/vertx-core-3.6.0/build.gradle +++ b/instrumentation/vertx-core-3.6.0/build.gradle @@ -13,7 +13,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-web:[3.6.0,3.8.0)' + passesOnly 'io.vertx:vertx-core:[3.6.0,3.8.0)' excludeRegex '.*CR[0-9]*' } diff --git a/instrumentation/vertx-core-3.8.0/build.gradle b/instrumentation/vertx-core-3.8.0/build.gradle index 3cb6bad4ee..7d7883bfaf 100644 --- a/instrumentation/vertx-core-3.8.0/build.gradle +++ b/instrumentation/vertx-core-3.8.0/build.gradle @@ -13,7 +13,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-web:[3.8.0,3.9.0)' + passesOnly 'io.vertx:vertx-core:[3.8.0,3.9.0)' excludeRegex '.*CR[0-9]*' excludeRegex '.*-milestone[0-9]' } diff --git a/instrumentation/vertx-core-3.9.0/build.gradle b/instrumentation/vertx-core-3.9.0/build.gradle index 13f7603af5..671b75e47d 100644 --- a/instrumentation/vertx-core-3.9.0/build.gradle +++ b/instrumentation/vertx-core-3.9.0/build.gradle @@ -13,7 +13,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-web:[3.9.0,4.0.0.Beta1)' + passesOnly 'io.vertx:vertx-core:[3.9.0,4.0.0.Beta1)' excludeRegex '.*CR[0-9]*' excludeRegex '.*-milestone[0-9]' } From a58b23b176e416e2328e2c09a14ececcd1f66ae2 Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Thu, 9 Nov 2023 11:34:28 -0500 Subject: [PATCH 10/16] v4.1.0 module --- instrumentation/vertx-core-4.0.0/build.gradle | 2 +- instrumentation/vertx-core-4.1.0/.gitignore | 42 ++ instrumentation/vertx-core-4.1.0/README.md | 42 ++ instrumentation/vertx-core-4.1.0/build.gradle | 25 ++ .../instrumentation/AsyncHandlerWrapper.java | 35 ++ .../HttpClientRequestPromiseWrapper.java | 158 +++++++ .../vertx/instrumentation/InboundWrapper.java | 32 ++ .../instrumentation/OutboundWrapper.java | 32 ++ .../PromiseHandlerWrapper.java | 36 ++ .../vertx/instrumentation/VertxCoreUtil.java | 73 ++++ .../impl/HttpClientImpl_Instrumentation.java | 18 + ...HttpClientRequestBase_Instrumentation.java | 99 +++++ ...HttpClientRequestImpl_Instrumentation.java | 62 +++ .../impl/AbstractContext_Instrumentation.java | 28 ++ .../impl/ContextImpl_Instrumentation.java | 29 ++ .../future/FutureImpl_Instrumentation.java | 43 ++ .../instrumentation/VertxBlockingTest.java | 59 +++ .../nr/vertx/instrumentation/VertxClient.java | 385 ++++++++++++++++++ .../nr/vertx/instrumentation/VertxFuture.java | 200 +++++++++ settings.gradle | 1 + 20 files changed, 1400 insertions(+), 1 deletion(-) create mode 100644 instrumentation/vertx-core-4.1.0/.gitignore create mode 100644 instrumentation/vertx-core-4.1.0/README.md create mode 100644 instrumentation/vertx-core-4.1.0/build.gradle create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java create mode 100644 instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java create mode 100644 instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java create mode 100644 instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java diff --git a/instrumentation/vertx-core-4.0.0/build.gradle b/instrumentation/vertx-core-4.0.0/build.gradle index e17b5f6b24..6dc20f91a9 100644 --- a/instrumentation/vertx-core-4.0.0/build.gradle +++ b/instrumentation/vertx-core-4.0.0/build.gradle @@ -13,7 +13,7 @@ dependencies { } verifyInstrumentation { - passesOnly 'io.vertx:vertx-core:[4.0.0,4.3.2)' + passesOnly 'io.vertx:vertx-core:[4.0.0,4.1.0)' excludeRegex '.*CR[0-9]*' excludeRegex '.*-milestone[0-9]' excludeRegex '.*Beta[0-9]' diff --git a/instrumentation/vertx-core-4.1.0/.gitignore b/instrumentation/vertx-core-4.1.0/.gitignore new file mode 100644 index 0000000000..b63da4551b --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/instrumentation/vertx-core-4.1.0/README.md b/instrumentation/vertx-core-4.1.0/README.md new file mode 100644 index 0000000000..36cd1a1c4d --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/README.md @@ -0,0 +1,42 @@ +Vert.x 4.00 Core Instrumentation +================================ + +### HTTP Client + +The Vert.x core instrumentation module is mainly concerned with instrumenting the low level Vert.x HTTP Client and associated Future instances. + +The instrumentation module can instrument client instances created various ways. For example, via inline callbacks: +```text + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.POST, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + HttpClientResponse response = respAsyncResult.result(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + // Handle body + } + }); + } + }); + } + }); +``` + +Or clients created that utilize Futures for response handling: +```text + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port, "localhost", "/", ar -> { + if (ar.succeeded()) { + HttpClientRequest request = ar.result(); + request.send("foo") + .onSuccess(response -> { + //Success handler + }).onFailure(err -> { + //Failure handler + }); + } + }); +``` diff --git a/instrumentation/vertx-core-4.1.0/build.gradle b/instrumentation/vertx-core-4.1.0/build.gradle new file mode 100644 index 0000000000..45305a96c1 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/build.gradle @@ -0,0 +1,25 @@ +jar { + manifest { + attributes 'Implementation-Title': 'com.newrelic.instrumentation.vertx-core-4.1.0' + } +} + + + +dependencies { + implementation(project(":agent-bridge")) + implementation("io.vertx:vertx-core:4.1.0") + testImplementation("io.vertx:vertx-core:4.1.0") +} + +verifyInstrumentation { + passesOnly 'io.vertx:vertx-core:[4.1.0,4.3.2)' + excludeRegex '.*CR[0-9]*' + excludeRegex '.*-milestone[0-9]' + excludeRegex '.*Beta[0-9]' +} + +site { + title 'Vertx' + type 'Framework' +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java new file mode 100644 index 0000000000..00f88bfb4d --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/AsyncHandlerWrapper.java @@ -0,0 +1,35 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; + +public class AsyncHandlerWrapper implements Handler> { + private Handler> original; + + private Token token; + + public AsyncHandlerWrapper(Handler> original, Token token) { + this.original = original; + this.token = token; + } + + @Override + @Trace(async = true, excludeFromTransactionTrace = true) + public void handle(AsyncResult event) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + this.original.handle(event); + this.original = null; + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java new file mode 100644 index 0000000000..4c577f7fe0 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java @@ -0,0 +1,158 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.Transaction; +import io.netty.util.concurrent.Future; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.impl.ContextInternal; +import io.vertx.core.impl.future.Listener; +import io.vertx.core.impl.future.PromiseInternal; + +import java.util.function.Function; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +public class HttpClientRequestPromiseWrapper implements PromiseInternal { + + private final PromiseInternal original; + + private Token token; + + public HttpClientRequestPromiseWrapper(PromiseInternal original, Token token) { + this.original = original; + this.token = token; + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public void complete(HttpClientRequest req) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + original.complete(req); + } + + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryFail(Throwable t) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (t.toString().contains("UnknownHostException") && txn != null) { + Segment segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + VertxCoreUtil.reportUnknownHost(segment); + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } + + return original.tryFail(t); + } + + @Override + public boolean tryComplete(HttpClientRequest result) { + return original.tryComplete(result); + } + + @Override + public io.vertx.core.Future future() { + return original.future(); + } + + public ContextInternal context() { + return original.context(); + } + + @Override + public void addListener(Listener listener) { + original.addListener(listener); + } + + @Override + public void operationComplete(Future future) throws Exception { + original.operationComplete(future); + } + + @Override + public boolean isComplete() { + return original.isComplete(); + } + + @Override + public io.vertx.core.Future onComplete(Handler> handler) { + return original.onComplete(handler); + } + + @Override + public HttpClientRequest result() { + return original.result(); + } + + @Override + public Throwable cause() { + return original.cause(); + } + + @Override + public boolean succeeded() { + return original.succeeded(); + } + + @Override + public boolean failed() { + return original.failed(); + } + + @Override + public io.vertx.core.Future compose(Function> successMapper, + Function> failureMapper) { + return original.compose(successMapper, failureMapper); + } + + @Override + public io.vertx.core.Future transform(Function, io.vertx.core.Future> mapper) { + return original.transform(mapper); + } + + @Override + public io.vertx.core.Future eventually(Function> mapper) { + return original.eventually(mapper); + } + + @Override + public io.vertx.core.Future map(Function mapper) { + return original.map(mapper); + } + + @Override + public io.vertx.core.Future map(V value) { + return original.map(value); + } + + @Override + public io.vertx.core.Future otherwise(Function mapper) { + return original.otherwise(mapper); + } + + @Override + public io.vertx.core.Future otherwise(HttpClientRequest value) { + return original.otherwise(value); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java new file mode 100644 index 0000000000..41531affba --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/InboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.InboundHeaders; +import io.vertx.core.http.HttpClientResponse; + +public class InboundWrapper implements InboundHeaders { + + private final HttpClientResponse response; + + public InboundWrapper(HttpClientResponse response) { + this.response = response; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public String getHeader(String name) { + return response.getHeader(name); + } + +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java new file mode 100644 index 0000000000..7f4765a262 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/OutboundWrapper.java @@ -0,0 +1,32 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.HeaderType; +import com.newrelic.api.agent.OutboundHeaders; +import io.vertx.core.MultiMap; + +public class OutboundWrapper implements OutboundHeaders { + + private final MultiMap headers; + + public OutboundWrapper(MultiMap headers) { + this.headers = headers; + } + + @Override + public HeaderType getHeaderType() { + return HeaderType.HTTP; + } + + @Override + public void setHeader(String name, String value) { + headers.add(name, value); + } + +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java new file mode 100644 index 0000000000..0749efd318 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/PromiseHandlerWrapper.java @@ -0,0 +1,36 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package com.nr.vertx.instrumentation; + +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Handler; +import io.vertx.core.Promise; + +public class PromiseHandlerWrapper implements Handler> { + + private Handler> original; + + private Token token; + + public PromiseHandlerWrapper(Handler> original, Token token) { + this.original = original; + this.token = token; + } + + @Override + @Trace(async = true, excludeFromTransactionTrace = true) + public void handle(Promise event) { + if (token != null) { + token.linkAndExpire(); + token = null; + } + + this.original.handle(event); + this.original = null; + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java new file mode 100644 index 0000000000..cb98790edb --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java @@ -0,0 +1,73 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.GenericParameters; +import com.newrelic.api.agent.HttpParameters; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Transaction; +import com.newrelic.api.agent.weaver.Weaver; +import io.vertx.core.Handler; +import io.vertx.core.http.impl.HttpClientResponseImpl; +import io.vertx.core.impl.future.Listener; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; + +public class VertxCoreUtil { + + private VertxCoreUtil() { + } + + private static final Map tokenMap = AgentBridge.collectionFactory.createConcurrentWeakKeyedMap(); + + public static final String VERTX_CLIENT = "Vertx-Client"; + public static final String END = "handleResponse"; + + private static final URI UNKNOWN_HOST_URI = URI.create("http://UnknownHost/"); + + public static void storeToken(Listener handler) { + if (handler != null && AgentBridge.getAgent().getTransaction(false) != null) { + tokenMap.put(handler, NewRelic.getAgent().getTransaction().getToken()); + } + } + + public static void linkAndExpireToken(Listener handler) { + if (handler != null) { + final Token token = tokenMap.remove(handler); + if (token != null) { + token.linkAndExpire(); + } + } + } + + public static void processResponse(Segment segment, HttpClientResponseImpl resp, String host, int port, + String scheme) { + try { + URI uri = new URI(scheme, null, host, port, null, null, null); + segment.reportAsExternal(HttpParameters.library(VERTX_CLIENT) + .uri(uri) + .procedure(END) + .inboundHeaders(new InboundWrapper(resp)) + .build()); + } catch (URISyntaxException e) { + AgentBridge.instrumentation.noticeInstrumentationError(e, Weaver.getImplementationTitle()); + } + } + + public static void reportUnknownHost(Segment segment) { + segment.reportAsExternal(GenericParameters.library(VERTX_CLIENT) + .uri(UNKNOWN_HOST_URI) + .procedure(END) + .build()); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java new file mode 100644 index 0000000000..9b7eff5baf --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java @@ -0,0 +1,18 @@ +package io.vertx.core.http.impl; + +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.MatchType; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.HttpClientRequestPromiseWrapper; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.RequestOptions; +import io.vertx.core.impl.future.PromiseInternal; + +@Weave(type = MatchType.BaseClass, originalName = "io.vertx.core.http.impl.HttpClientImpl") +public class HttpClientImpl_Instrumentation { + private void doRequest(RequestOptions request, PromiseInternal requestPromise) { + requestPromise = new HttpClientRequestPromiseWrapper(requestPromise, NewRelic.getAgent().getTransaction().getToken()); + Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java new file mode 100644 index 0000000000..a59e0a2c74 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -0,0 +1,99 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.http.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Segment; +import com.newrelic.api.agent.Token; +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.Transaction; +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.vertx.instrumentation.AsyncHandlerWrapper; +import com.nr.vertx.instrumentation.OutboundWrapper; +import com.nr.vertx.instrumentation.VertxCoreUtil; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.MultiMap; +import io.vertx.core.Promise; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; + +import java.net.UnknownHostException; +import java.util.logging.Level; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +@Weave(type = MatchType.BaseClass, originalName = "io.vertx.core.http.impl.HttpClientRequestBase") +public abstract class HttpClientRequestBase_Instrumentation { + + @NewField + public Segment segment; + + public abstract MultiMap headers(); + +// @Trace(async = true) +// public HttpClientRequest response(Handler> handler) { +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response " + handler); +// if (AgentBridge.getAgent().getTransaction(false) != null) { +// this.token = NewRelic.getAgent().getTransaction().getToken(); +// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); +// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response"); +// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); +// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); +// } +// +// return Weaver.callOriginal(); +// } + + @Trace(async = true) + void handleResponse(Promise promise, HttpClientResponse resp, long timeoutMs) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse()"); + if (segment != null) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse() segment != null && this.token != null"); + + final Token segmentToken = segment.getTransaction().getToken(); + reportExternal(resp, segment); + segment.end(); + segmentToken.linkAndExpire(); + + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } + Weaver.callOriginal(); + } + + @Trace(async = true) + void handleException(Throwable t) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleException()"); + if (segment != null) { + if (t instanceof UnknownHostException) { + VertxCoreUtil.reportUnknownHost(segment); + } + final Token token = segment.getTransaction().getToken(); + segment.end(); + token.linkAndExpire(); + + AgentBridge.getAgent().getTransaction(false).expireAllTokens(); + } + Weaver.callOriginal(); + } + + private void reportExternal(HttpClientResponse response, Segment segment) { + if (response instanceof HttpClientResponseImpl) { + HttpClientResponseImpl resp = (HttpClientResponseImpl) response; + final String host = resp.request().getHost(); + final int port = resp.request().getPort(); + final String scheme = resp.request().ssl ? "https" : "http"; + VertxCoreUtil.processResponse(segment, resp, host, port, scheme); + } + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java new file mode 100644 index 0000000000..f5ea66c890 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java @@ -0,0 +1,62 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.http.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.OutboundWrapper; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; + +import java.util.logging.Level; + +import static com.nr.vertx.instrumentation.VertxCoreUtil.END; +import static com.nr.vertx.instrumentation.VertxCoreUtil.VERTX_CLIENT; + +@Weave(originalName = "io.vertx.core.http.impl.HttpClientRequestImpl") +public abstract class HttpClientRequestImpl_Instrumentation extends HttpClientRequestBase_Instrumentation { + + public Future end(Buffer chunk) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk"); + if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk 2"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + return Weaver.callOriginal(); + } + + public void end(Buffer chunk, Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler"); + if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler 2"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + } + Weaver.callOriginal(); + } + + public void end(Handler> handler) { + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler)"); + if (AgentBridge.getAgent().getTransaction(false) != null) { + System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); + AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler) 2"); + segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); + System.out.println("!!!!segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segment.getTransaction().getToken()); + segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); + System.out.println("end(Handler> Segment -- " + segment); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java new file mode 100644 index 0000000000..3570a9a149 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/AbstractContext_Instrumentation.java @@ -0,0 +1,28 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ +package io.vertx.core.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Transaction; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.AsyncHandlerWrapper; +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; + +@Weave(originalName = "io.vertx.core.impl.AbstractContext") +abstract class AbstractContext_Instrumentation { + private static void setResultHandler(ContextInternal ctx, Future fut, Handler> resultHandler) { + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (txn != null) { + resultHandler = new AsyncHandlerWrapper<>(resultHandler, NewRelic.getAgent().getTransaction().getToken()); + } + Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java new file mode 100644 index 0000000000..32f0cff9ab --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/ContextImpl_Instrumentation.java @@ -0,0 +1,29 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.impl; + +import com.newrelic.agent.bridge.AgentBridge; +import com.newrelic.agent.bridge.Transaction; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.PromiseHandlerWrapper; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.Promise; + +@Weave(originalName = "io.vertx.core.impl.ContextImpl") +abstract class ContextImpl_Instrumentation { + static Future executeBlocking(ContextInternal context, Handler> blockingCodeHandler, WorkerPool workerPool, TaskQueue queue) { + Transaction txn = AgentBridge.getAgent().getTransaction(false); + if (txn != null) { + blockingCodeHandler = new PromiseHandlerWrapper(blockingCodeHandler, NewRelic.getAgent().getTransaction().getToken()); + } + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java new file mode 100644 index 0000000000..c143d6ca2c --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/impl/future/FutureImpl_Instrumentation.java @@ -0,0 +1,43 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package io.vertx.core.impl.future; + +import com.newrelic.api.agent.Trace; +import com.newrelic.api.agent.weaver.Weave; +import com.newrelic.api.agent.weaver.Weaver; +import com.nr.vertx.instrumentation.VertxCoreUtil; + +@Weave(originalName = "io.vertx.core.impl.future.FutureImpl") +abstract class FutureImpl_Instrumentation { + + private Listener listener; + + public abstract boolean isComplete(); + + @Trace(async = true, excludeFromTransactionTrace = true) + public void addListener(Listener listener) { + if (isComplete()) { + VertxCoreUtil.linkAndExpireToken(listener); + } else { + VertxCoreUtil.storeToken(listener); + } + Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryComplete(Object result) { + VertxCoreUtil.linkAndExpireToken(this.listener); + return Weaver.callOriginal(); + } + + @Trace(async = true, excludeFromTransactionTrace = true) + public boolean tryFail(Throwable cause) { + VertxCoreUtil.linkAndExpireToken(this.listener); + return Weaver.callOriginal(); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java new file mode 100644 index 0000000000..0fa7ac126a --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxBlockingTest.java @@ -0,0 +1,59 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Vertx; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxBlockingTest { + + @Test + public void executeBlocking_withPromiseAndResult() throws InterruptedException { + Vertx vertx = Vertx.vertx(); + try { + executeBlocking(vertx); + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxBlockingTest/executeBlocking"; + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("InFuture")); + assertTrue(attributes.containsKey("InResponseHandler")); + } finally { + vertx.close(); + } + } + + @Trace(dispatcher = true) + void executeBlocking(Vertx vertx) throws InterruptedException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + vertx.executeBlocking(future -> { + NewRelic.addCustomParameter("InFuture", "yes"); + future.complete(); + }, res -> { + NewRelic.addCustomParameter("InResponseHandler", "yes"); + countDownLatch.countDown(); + }); + countDownLatch.await(); + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java new file mode 100644 index 0000000000..6b87f6b37e --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java @@ -0,0 +1,385 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.CatHelper; +import com.newrelic.agent.introspec.ExternalRequest; +import com.newrelic.agent.introspec.HttpTestServer; +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.MetricsHelper; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.agent.introspec.internal.HttpServerLocator; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.Collection; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxClient { + + private static int port; + private static Future server; + private static Vertx vertx; + + @BeforeClass + public static void beforeClass() { + port = getAvailablePort(); + vertx = Vertx.vertx(); + + server = vertx.createHttpServer().requestHandler(request -> { + final String statusCode = request.getHeader("statusCode"); + if (statusCode == null) { + System.out.println("statusCode is null"); + request.response().end("response"); + } else { + System.out.println("statusCode is NOT null -- " + statusCode); + if (request.absoluteURI().equals("/redirect")) { + request.headers().clear(); + request.response().putHeader("Location", "http://localhost:" + port + "/other"); + } + request.response().setStatusCode(Integer.parseInt(statusCode)).end("response"); + } + }).listen(port); + } + + @AfterClass + public static void afterClass() { + server.result().close(); + vertx.close(); + } + + @Test + public void testGet_withCallbacks() throws InterruptedException { + getCall_withCallbacks(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbacks", "localhost"); + } + + @Trace(dispatcher = true) + public void getCall_withCallbacks() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } else { + // Handle server error, for example, connection closed + } + }); + } else { + // Handle server error, for example, connection closed + } + }); + } else { + // Connection error, for example, invalid server or invalid SSL certificate + } + }); + latch.await(); + } + + @Test + public void testGet_withCallbackAndFutures() throws InterruptedException { + getCall_withCallbackAndFutures(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbackAndFutures", "localhost"); + } + + @Trace(dispatcher = true) + public void getCall_withCallbackAndFutures() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + + httpClient.request(HttpMethod.GET, port, "localhost", "/", ar -> { + if (ar.succeeded()) { + HttpClientRequest request = ar.result(); + request.send("foo") + .onSuccess(response -> { + NewRelic.addCustomParameter("responseHandler", "true"); + NewRelic.addCustomParameter("bodyHandler", "true"); + latch.countDown(); + }).onFailure(err -> { + // + }); + } + }); + latch.await(); + } + + @Test + public void testPost() throws InterruptedException { + postCall(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/postCall", "localhost"); + } + + @Trace(dispatcher = true) + private void postCall() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + + httpClient.request(HttpMethod.POST, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); + latch.await(); + } + + @Test + public void testRedirect() throws InterruptedException { + redirect(); + // Wait for transaction to finish + InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/redirect", "localhost"); + } + + @Trace(dispatcher = true) + public void redirect() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.putHeader("statusCode", "301"); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); + latch.await(); + } + + @Test + public void testCat() throws Exception { + try (HttpTestServer httpServer = HttpServerLocator.createAndStart()) { + cat(httpServer); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + String host = httpServer.getEndPoint().getHost(); + String txName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/cat"; + assertEquals(2, introspector.getFinishedTransactionCount(250)); + Collection names = introspector.getTransactionNames(); + assertEquals(2, names.size()); + assertTrue(names.contains(httpServer.getServerTransactionName())); + assertTrue(names.contains(txName)); + + // scoped metrics + assertEquals(1, MetricsHelper.getScopedMetricCount(txName, "ExternalTransaction/" + host + "/" + + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); + assertEquals(1, MetricsHelper.getScopedMetricCount(txName, + "Java/com.nr.vertx.instrumentation.VertxClient/cat")); + + // unscoped metrics + assertEquals(1, MetricsHelper.getUnscopedMetricCount("ExternalTransaction/" + host + "/" + + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/" + host + "/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); + + // events + Collection transactionEvents = introspector.getTransactionEvents(txName); + assertEquals(1, transactionEvents.size()); + TransactionEvent transactionEvent = transactionEvents.iterator().next(); + assertEquals(1, transactionEvent.getExternalCallCount()); + assertTrue(transactionEvent.getExternalDurationInSec() > 0); + + CatHelper.verifyOneSuccessfulCat(introspector, txName); + + // external request information + Collection externalRequests = introspector.getExternalRequests(txName); + assertEquals(1, externalRequests.size()); + ExternalRequest externalRequest = externalRequests.iterator().next(); + assertEquals(1, externalRequest.getCount()); + assertEquals(host, externalRequest.getHostname()); + } + } + + @Trace(dispatcher = true) + public void cat(HttpTestServer httpServer) throws Exception { + try { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, httpServer.getEndPoint().getPort(),httpServer.getEndPoint().getHost(), "/", reqAsyncResult -> { + if (reqAsyncResult.succeeded()) { //Request object successfully created + HttpClientRequest request = reqAsyncResult.result(); + request.putHeader(HttpTestServer.DO_CAT, "true"); + request.send(respAsyncResult -> { //Sending the request + if (respAsyncResult.succeeded()) { + NewRelic.addCustomParameter("responseHandler", "true"); + HttpClientResponse response = respAsyncResult.result(); + latch.countDown(); + response.body(respBufferAsyncResult -> { //Retrieve response + if (respBufferAsyncResult.succeeded()) { + NewRelic.addCustomParameter("bodyHandler", "true"); + // Handle body + } + }); + } + }); + } + }); + latch.await(); + } finally { + httpServer.shutdown(); + } + } + + @Test + public void testUnknownHost_withCallbacks() throws Exception { + unknownHost_withCallbacks(); + assertUnknownHostExternal(); + } + + @Trace(dispatcher = true) + private void unknownHost_withCallbacks() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/", ar -> { + if (ar.failed()) { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + } + }); + latch.await(); + } + + @Test + public void testUnknownHost_withReturnedFuture() throws Exception { + unknownHost_withReturnedFuture(); + assertUnknownHostExternal(); + } + + @Trace(dispatcher = true) + private void unknownHost_withReturnedFuture() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + HttpClient httpClient = vertx.createHttpClient(); + + Future r = httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/"); + r.onFailure(err -> { + NewRelic.addCustomParameter("exceptionHandler", "true"); + latch.countDown(); + }); + + latch.await(); + } + + private void assertUnknownHostExternal() { + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(250)); + + final String txn = introspector.getTransactionNames().iterator().next(); + assertNotNull("Transaction not found", txn); + + assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/handleResponse")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/handleResponse")); + + // Unknown hosts generate no external rollups + assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/all")); + + // Make sure exception handler is linked + final TransactionEvent event = introspector.getTransactionEvents(txn).iterator().next(); + assertTrue(event.getAttributes().containsKey("exceptionHandler")); + } + + private void assertExternal(String transactionName, String host) { + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + Collection externalRequests = introspector.getExternalRequests(transactionName); + ExternalRequest request = externalRequests.iterator().next(); + assertEquals(host, request.getHostname()); + assertEquals("Vertx-Client", request.getLibrary()); + assertEquals("handleResponse", request.getOperation()); + Collection events = introspector.getTransactionEvents(transactionName); + TransactionEvent event = events.iterator().next(); + assertTrue(event.getAttributes().containsKey("responseHandler")); + + assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/handleResponse")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/handleResponse")); + + Collection transactionEvents = introspector.getTransactionEvents(transactionName); + assertEquals(1, transactionEvents.size()); + TransactionEvent transactionEvent = transactionEvents.iterator().next(); + assertEquals(1, transactionEvent.getExternalCallCount()); + assertTrue(transactionEvent.getExternalDurationInSec() > 0); + + // external rollups + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); + assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); + } + + private static int getAvailablePort() { + int port; + + try { + ServerSocket socket = new ServerSocket(0); + port = socket.getLocalPort(); + socket.close(); + } catch (IOException e) { + throw new RuntimeException("Unable to allocate ephemeral port"); + } + return port; + } +} diff --git a/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java new file mode 100644 index 0000000000..f3676b8fd0 --- /dev/null +++ b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java @@ -0,0 +1,200 @@ +/* + * + * * Copyright 2020 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + +package com.nr.vertx.instrumentation; + +import com.newrelic.agent.introspec.InstrumentationTestConfig; +import com.newrelic.agent.introspec.InstrumentationTestRunner; +import com.newrelic.agent.introspec.Introspector; +import com.newrelic.agent.introspec.TransactionEvent; +import com.newrelic.api.agent.NewRelic; +import com.newrelic.api.agent.Trace; +import io.vertx.core.CompositeFuture; +import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(InstrumentationTestRunner.class) +@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) +public class VertxFuture { + + @Test + public void testCompositeFuture() throws InterruptedException { + compositeFuturesAllFuturesSucceed(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFuturesAllFuturesSucceed"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("compositeFuture")); + } + + @Trace(dispatcher = true) + private void compositeFuturesAllFuturesSucceed() throws InterruptedException { + Vertx vertx = Vertx.vertx(); + CountDownLatch latch = new CountDownLatch(1); + Promise promise1 = Promise.promise(); + Promise promise2 = Promise.promise(); + + CompositeFuture.all(promise1.future(), promise2.future()).onComplete((ar -> { + if (ar.succeeded()) { + NewRelic.addCustomParameter("compositeFuture", "yes"); + } + latch.countDown(); + })); + + vertx.setTimer(1, handler -> { + promise1.complete("promise1"); + promise2.complete("promise2"); + }); + + latch.await(); + } + + @Test + public void whenFutureFails_withThrowable_txnStillCompletes() throws InterruptedException { + failFutureWithThrowable(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithThrowable"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureFails_withString_txnStillCompletes() throws InterruptedException { + failFutureWithString(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithString"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureCompletes_txnStillCompletes() throws InterruptedException { + completeFutureSuccessfully(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfully"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Test + public void whenFutureCompletes_withOnCompleteRegistered_txnStillCompletes() throws InterruptedException { + completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback(); + + Introspector introspector = InstrumentationTestRunner.getIntrospector(); + assertEquals(1, introspector.getFinishedTransactionCount(500)); + + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback"; + TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); + Map attributes = txnEvent.getAttributes(); + assertTrue(attributes.containsKey("future")); + } + + @Trace(dispatcher = true) + private void failFutureWithString() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onFailure(ar -> { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.fail("oops"); + }); + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void failFutureWithThrowable() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onFailure(ar -> { + NewRelic.addCustomParameter("future", "failed"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.fail(new IllegalArgumentException("foo")); + }); + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void completeFutureSuccessfully() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onSuccess(ar -> { + NewRelic.addCustomParameter("future", "success"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.complete("hooray"); + }); + + + countDownLatch.await(); + } + + @Trace(dispatcher = true) + private void completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + Vertx vertx = Vertx.vertx(); + Promise promise = Promise.promise(); + + Future future = promise.future(); + future.onComplete(ar -> { + NewRelic.addCustomParameter("future", "onComplete"); + countDownLatch.countDown(); + }); + + vertx.setTimer(1, handler -> { + promise.complete("hooray"); + }); + + countDownLatch.await(); + } +} diff --git a/settings.gradle b/settings.gradle index 108eb2dca5..fe479c6651 100644 --- a/settings.gradle +++ b/settings.gradle @@ -382,6 +382,7 @@ include 'instrumentation:vertx-core-3.6.0' include 'instrumentation:vertx-core-3.8.0' include 'instrumentation:vertx-core-3.9.0' include 'instrumentation:vertx-core-4.0.0' +include 'instrumentation:vertx-core-4.1.0' include 'instrumentation:vertx-core-4.3.2' include 'instrumentation:zio' From 8f4c9bc6dc1949bcd503e930225186ce6d7a8e75 Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Mon, 13 Nov 2023 13:48:48 -0500 Subject: [PATCH 11/16] rename tests; cleanup module source code --- ...HttpClientRequestBase_Instrumentation.java | 22 ++++--------------- ...HttpClientRequestImpl_Instrumentation.java | 9 -------- ...{VertxClient.java => VertxClientTest.java} | 14 ++++++------ .../instrumentation/VertxFutureTest.java} | 12 +++++----- ...HttpClientRequestBase_Instrumentation.java | 18 --------------- ...HttpClientRequestImpl_Instrumentation.java | 10 --------- .../instrumentation/VertxClientTest.java} | 14 ++++++------ .../instrumentation/VertxFutureTest.java} | 12 +++++----- .../instrumentation/VertxClientTest.java} | 14 ++++++------ .../instrumentation/VertxFutureTest.java} | 12 +++++----- 10 files changed, 43 insertions(+), 94 deletions(-) rename instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/{VertxClient.java => VertxClientTest.java} (98%) rename instrumentation/{vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java => vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java} (94%) rename instrumentation/{vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java => vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java} (98%) rename instrumentation/{vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java => vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java} (94%) rename instrumentation/{vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java => vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java} (98%) rename instrumentation/{vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java => vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java} (94%) diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java index a59e0a2c74..12b917d189 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -41,26 +41,9 @@ public abstract class HttpClientRequestBase_Instrumentation { public abstract MultiMap headers(); -// @Trace(async = true) -// public HttpClientRequest response(Handler> handler) { -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response " + handler); -// if (AgentBridge.getAgent().getTransaction(false) != null) { -// this.token = NewRelic.getAgent().getTransaction().getToken(); -// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response"); -// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); -// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); -// } -// -// return Weaver.callOriginal(); -// } - @Trace(async = true) void handleResponse(Promise promise, HttpClientResponse resp, long timeoutMs) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse()"); if (segment != null) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse() segment != null && this.token != null"); - final Token segmentToken = segment.getTransaction().getToken(); reportExternal(resp, segment); segment.end(); @@ -73,7 +56,6 @@ void handleResponse(Promise promise, HttpClientResponse resp @Trace(async = true) void handleException(Throwable t) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleException()"); if (segment != null) { if (t instanceof UnknownHostException) { VertxCoreUtil.reportUnknownHost(segment); @@ -82,6 +64,10 @@ void handleException(Throwable t) { segment.end(); token.linkAndExpire(); + // This is required since Vertx seems to create various promises behind the scenes + // (specifically on client/server creation) which don't get resolved until the + // client/server is closed, which might never happen since they're longed lived instances. + // The expireAllTokens insures the transaction doesn't hang because of dangling token instances. AgentBridge.getAgent().getTransaction(false).expireAllTokens(); } Weaver.callOriginal(); diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java index f5ea66c890..8ad2d19807 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java @@ -28,7 +28,6 @@ public abstract class HttpClientRequestImpl_Instrumentation extends HttpClientRe public Future end(Buffer chunk) { AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk"); if (AgentBridge.getAgent().getTransaction(false) != null) { - System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk 2"); segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); @@ -37,10 +36,7 @@ public Future end(Buffer chunk) { } public void end(Buffer chunk, Handler> handler) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler"); if (AgentBridge.getAgent().getTransaction(false) != null) { - System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler 2"); segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); } @@ -48,14 +44,9 @@ public void end(Buffer chunk, Handler> handler) { } public void end(Handler> handler) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler)"); if (AgentBridge.getAgent().getTransaction(false) != null) { - System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler) 2"); segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); - System.out.println("!!!!segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segment.getTransaction().getToken()); segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); - System.out.println("end(Handler> Segment -- " + segment); } Weaver.callOriginal(); } diff --git a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java similarity index 98% rename from instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java rename to instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java index 6b87f6b37e..fb9ed1d527 100644 --- a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java +++ b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java @@ -41,7 +41,7 @@ @RunWith(InstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = { "io.vertx" }) -public class VertxClient { +public class VertxClientTest { private static int port; private static Future server; @@ -79,7 +79,7 @@ public void testGet_withCallbacks() throws InterruptedException { getCall_withCallbacks(); // Wait for transaction to finish InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbacks", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/getCall_withCallbacks", "localhost"); } @Trace(dispatcher = true) @@ -119,7 +119,7 @@ public void testGet_withCallbackAndFutures() throws InterruptedException { getCall_withCallbackAndFutures(); // Wait for transaction to finish InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbackAndFutures", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/getCall_withCallbackAndFutures", "localhost"); } @Trace(dispatcher = true) @@ -148,7 +148,7 @@ public void testPost() throws InterruptedException { postCall(); // Wait for transaction to finish InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/postCall", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/postCall", "localhost"); } @Trace(dispatcher = true) @@ -182,7 +182,7 @@ public void testRedirect() throws InterruptedException { redirect(); // Wait for transaction to finish InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/redirect", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/redirect", "localhost"); } @Trace(dispatcher = true) @@ -218,7 +218,7 @@ public void testCat() throws Exception { Introspector introspector = InstrumentationTestRunner.getIntrospector(); String host = httpServer.getEndPoint().getHost(); - String txName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/cat"; + String txName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/cat"; assertEquals(2, introspector.getFinishedTransactionCount(250)); Collection names = introspector.getTransactionNames(); assertEquals(2, names.size()); @@ -229,7 +229,7 @@ public void testCat() throws Exception { assertEquals(1, MetricsHelper.getScopedMetricCount(txName, "ExternalTransaction/" + host + "/" + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); assertEquals(1, MetricsHelper.getScopedMetricCount(txName, - "Java/com.nr.vertx.instrumentation.VertxClient/cat")); + "Java/com.nr.vertx.instrumentation.VertxClientTest/cat")); // unscoped metrics assertEquals(1, MetricsHelper.getUnscopedMetricCount("ExternalTransaction/" + host + "/" diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java similarity index 94% rename from instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java rename to instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java index f3676b8fd0..5cc5440ce3 100644 --- a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java +++ b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java @@ -28,7 +28,7 @@ @RunWith(InstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = { "io.vertx" }) -public class VertxFuture { +public class VertxFutureTest { @Test public void testCompositeFuture() throws InterruptedException { @@ -37,7 +37,7 @@ public void testCompositeFuture() throws InterruptedException { Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFuturesAllFuturesSucceed"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/compositeFuturesAllFuturesSucceed"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("compositeFuture")); @@ -72,7 +72,7 @@ public void whenFutureFails_withThrowable_txnStillCompletes() throws Interrupted Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithThrowable"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/failFutureWithThrowable"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); @@ -85,7 +85,7 @@ public void whenFutureFails_withString_txnStillCompletes() throws InterruptedExc Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithString"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/failFutureWithString"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); @@ -98,7 +98,7 @@ public void whenFutureCompletes_txnStillCompletes() throws InterruptedException Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfully"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/completeFutureSuccessfully"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); @@ -111,7 +111,7 @@ public void whenFutureCompletes_withOnCompleteRegistered_txnStillCompletes() thr Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java index a59e0a2c74..3f26f77489 100644 --- a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -41,26 +41,9 @@ public abstract class HttpClientRequestBase_Instrumentation { public abstract MultiMap headers(); -// @Trace(async = true) -// public HttpClientRequest response(Handler> handler) { -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response " + handler); -// if (AgentBridge.getAgent().getTransaction(false) != null) { -// this.token = NewRelic.getAgent().getTransaction().getToken(); -// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response"); -// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); -// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); -// } -// -// return Weaver.callOriginal(); -// } - @Trace(async = true) void handleResponse(Promise promise, HttpClientResponse resp, long timeoutMs) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse()"); if (segment != null) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse() segment != null && this.token != null"); - final Token segmentToken = segment.getTransaction().getToken(); reportExternal(resp, segment); segment.end(); @@ -73,7 +56,6 @@ void handleResponse(Promise promise, HttpClientResponse resp @Trace(async = true) void handleException(Throwable t) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleException()"); if (segment != null) { if (t instanceof UnknownHostException) { VertxCoreUtil.reportUnknownHost(segment); diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java index f5ea66c890..8265c3d713 100644 --- a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java @@ -26,9 +26,7 @@ public abstract class HttpClientRequestImpl_Instrumentation extends HttpClientRequestBase_Instrumentation { public Future end(Buffer chunk) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk"); if (AgentBridge.getAgent().getTransaction(false) != null) { - System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk 2"); segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); @@ -37,10 +35,7 @@ public Future end(Buffer chunk) { } public void end(Buffer chunk, Handler> handler) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler"); if (AgentBridge.getAgent().getTransaction(false) != null) { - System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk, Handler> handler 2"); segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); } @@ -48,14 +43,9 @@ public void end(Buffer chunk, Handler> handler) { } public void end(Handler> handler) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler)"); if (AgentBridge.getAgent().getTransaction(false) != null) { - System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Handler> handler) 2"); segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); - System.out.println("!!!!segment -- " + segment + " txn: " + segment.getTransaction() + " token: " + segment.getTransaction().getToken()); segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); - System.out.println("end(Handler> Segment -- " + segment); } Weaver.callOriginal(); } diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java similarity index 98% rename from instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java rename to instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java index 6b87f6b37e..fb9ed1d527 100644 --- a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java +++ b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java @@ -41,7 +41,7 @@ @RunWith(InstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = { "io.vertx" }) -public class VertxClient { +public class VertxClientTest { private static int port; private static Future server; @@ -79,7 +79,7 @@ public void testGet_withCallbacks() throws InterruptedException { getCall_withCallbacks(); // Wait for transaction to finish InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbacks", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/getCall_withCallbacks", "localhost"); } @Trace(dispatcher = true) @@ -119,7 +119,7 @@ public void testGet_withCallbackAndFutures() throws InterruptedException { getCall_withCallbackAndFutures(); // Wait for transaction to finish InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbackAndFutures", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/getCall_withCallbackAndFutures", "localhost"); } @Trace(dispatcher = true) @@ -148,7 +148,7 @@ public void testPost() throws InterruptedException { postCall(); // Wait for transaction to finish InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/postCall", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/postCall", "localhost"); } @Trace(dispatcher = true) @@ -182,7 +182,7 @@ public void testRedirect() throws InterruptedException { redirect(); // Wait for transaction to finish InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/redirect", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/redirect", "localhost"); } @Trace(dispatcher = true) @@ -218,7 +218,7 @@ public void testCat() throws Exception { Introspector introspector = InstrumentationTestRunner.getIntrospector(); String host = httpServer.getEndPoint().getHost(); - String txName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/cat"; + String txName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/cat"; assertEquals(2, introspector.getFinishedTransactionCount(250)); Collection names = introspector.getTransactionNames(); assertEquals(2, names.size()); @@ -229,7 +229,7 @@ public void testCat() throws Exception { assertEquals(1, MetricsHelper.getScopedMetricCount(txName, "ExternalTransaction/" + host + "/" + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); assertEquals(1, MetricsHelper.getScopedMetricCount(txName, - "Java/com.nr.vertx.instrumentation.VertxClient/cat")); + "Java/com.nr.vertx.instrumentation.VertxClientTest/cat")); // unscoped metrics assertEquals(1, MetricsHelper.getUnscopedMetricCount("ExternalTransaction/" + host + "/" diff --git a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java similarity index 94% rename from instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java rename to instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java index f3676b8fd0..5cc5440ce3 100644 --- a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java +++ b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java @@ -28,7 +28,7 @@ @RunWith(InstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = { "io.vertx" }) -public class VertxFuture { +public class VertxFutureTest { @Test public void testCompositeFuture() throws InterruptedException { @@ -37,7 +37,7 @@ public void testCompositeFuture() throws InterruptedException { Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFuturesAllFuturesSucceed"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/compositeFuturesAllFuturesSucceed"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("compositeFuture")); @@ -72,7 +72,7 @@ public void whenFutureFails_withThrowable_txnStillCompletes() throws Interrupted Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithThrowable"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/failFutureWithThrowable"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); @@ -85,7 +85,7 @@ public void whenFutureFails_withString_txnStillCompletes() throws InterruptedExc Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithString"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/failFutureWithString"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); @@ -98,7 +98,7 @@ public void whenFutureCompletes_txnStillCompletes() throws InterruptedException Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfully"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/completeFutureSuccessfully"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); @@ -111,7 +111,7 @@ public void whenFutureCompletes_withOnCompleteRegistered_txnStillCompletes() thr Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); diff --git a/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java similarity index 98% rename from instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java rename to instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java index 6b87f6b37e..fb9ed1d527 100644 --- a/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClient.java +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java @@ -41,7 +41,7 @@ @RunWith(InstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = { "io.vertx" }) -public class VertxClient { +public class VertxClientTest { private static int port; private static Future server; @@ -79,7 +79,7 @@ public void testGet_withCallbacks() throws InterruptedException { getCall_withCallbacks(); // Wait for transaction to finish InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbacks", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/getCall_withCallbacks", "localhost"); } @Trace(dispatcher = true) @@ -119,7 +119,7 @@ public void testGet_withCallbackAndFutures() throws InterruptedException { getCall_withCallbackAndFutures(); // Wait for transaction to finish InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbackAndFutures", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/getCall_withCallbackAndFutures", "localhost"); } @Trace(dispatcher = true) @@ -148,7 +148,7 @@ public void testPost() throws InterruptedException { postCall(); // Wait for transaction to finish InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/postCall", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/postCall", "localhost"); } @Trace(dispatcher = true) @@ -182,7 +182,7 @@ public void testRedirect() throws InterruptedException { redirect(); // Wait for transaction to finish InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/redirect", "localhost"); + assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/redirect", "localhost"); } @Trace(dispatcher = true) @@ -218,7 +218,7 @@ public void testCat() throws Exception { Introspector introspector = InstrumentationTestRunner.getIntrospector(); String host = httpServer.getEndPoint().getHost(); - String txName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/cat"; + String txName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClientTest/cat"; assertEquals(2, introspector.getFinishedTransactionCount(250)); Collection names = introspector.getTransactionNames(); assertEquals(2, names.size()); @@ -229,7 +229,7 @@ public void testCat() throws Exception { assertEquals(1, MetricsHelper.getScopedMetricCount(txName, "ExternalTransaction/" + host + "/" + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); assertEquals(1, MetricsHelper.getScopedMetricCount(txName, - "Java/com.nr.vertx.instrumentation.VertxClient/cat")); + "Java/com.nr.vertx.instrumentation.VertxClientTest/cat")); // unscoped metrics assertEquals(1, MetricsHelper.getUnscopedMetricCount("ExternalTransaction/" + host + "/" diff --git a/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java similarity index 94% rename from instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java rename to instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java index f3676b8fd0..5cc5440ce3 100644 --- a/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java +++ b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java @@ -28,7 +28,7 @@ @RunWith(InstrumentationTestRunner.class) @InstrumentationTestConfig(includePrefixes = { "io.vertx" }) -public class VertxFuture { +public class VertxFutureTest { @Test public void testCompositeFuture() throws InterruptedException { @@ -37,7 +37,7 @@ public void testCompositeFuture() throws InterruptedException { Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFuturesAllFuturesSucceed"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/compositeFuturesAllFuturesSucceed"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("compositeFuture")); @@ -72,7 +72,7 @@ public void whenFutureFails_withThrowable_txnStillCompletes() throws Interrupted Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithThrowable"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/failFutureWithThrowable"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); @@ -85,7 +85,7 @@ public void whenFutureFails_withString_txnStillCompletes() throws InterruptedExc Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithString"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/failFutureWithString"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); @@ -98,7 +98,7 @@ public void whenFutureCompletes_txnStillCompletes() throws InterruptedException Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfully"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/completeFutureSuccessfully"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); @@ -111,7 +111,7 @@ public void whenFutureCompletes_withOnCompleteRegistered_txnStillCompletes() thr Introspector introspector = InstrumentationTestRunner.getIntrospector(); assertEquals(1, introspector.getFinishedTransactionCount(500)); - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback"; + String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFutureTest/completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback"; TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); Map attributes = txnEvent.getAttributes(); assertTrue(attributes.containsKey("future")); From f58e03d6a089f0d2a82e3b42cc845f70a5dd40a5 Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Mon, 13 Nov 2023 14:23:35 -0500 Subject: [PATCH 12/16] remove old test classes --- .../nr/vertx/instrumentation/VertxClient.java | 385 ------------------ .../nr/vertx/instrumentation/VertxFuture.java | 200 --------- 2 files changed, 585 deletions(-) delete mode 100644 instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java delete mode 100644 instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java deleted file mode 100644 index 6b87f6b37e..0000000000 --- a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxClient.java +++ /dev/null @@ -1,385 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package com.nr.vertx.instrumentation; - -import com.newrelic.agent.introspec.CatHelper; -import com.newrelic.agent.introspec.ExternalRequest; -import com.newrelic.agent.introspec.HttpTestServer; -import com.newrelic.agent.introspec.InstrumentationTestConfig; -import com.newrelic.agent.introspec.InstrumentationTestRunner; -import com.newrelic.agent.introspec.Introspector; -import com.newrelic.agent.introspec.MetricsHelper; -import com.newrelic.agent.introspec.TransactionEvent; -import com.newrelic.agent.introspec.internal.HttpServerLocator; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Trace; -import io.vertx.core.Future; -import io.vertx.core.Vertx; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientRequest; -import io.vertx.core.http.HttpClientResponse; -import io.vertx.core.http.HttpMethod; -import io.vertx.core.http.HttpServer; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.IOException; -import java.net.ServerSocket; -import java.util.Collection; -import java.util.concurrent.CountDownLatch; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -@RunWith(InstrumentationTestRunner.class) -@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) -public class VertxClient { - - private static int port; - private static Future server; - private static Vertx vertx; - - @BeforeClass - public static void beforeClass() { - port = getAvailablePort(); - vertx = Vertx.vertx(); - - server = vertx.createHttpServer().requestHandler(request -> { - final String statusCode = request.getHeader("statusCode"); - if (statusCode == null) { - System.out.println("statusCode is null"); - request.response().end("response"); - } else { - System.out.println("statusCode is NOT null -- " + statusCode); - if (request.absoluteURI().equals("/redirect")) { - request.headers().clear(); - request.response().putHeader("Location", "http://localhost:" + port + "/other"); - } - request.response().setStatusCode(Integer.parseInt(statusCode)).end("response"); - } - }).listen(port); - } - - @AfterClass - public static void afterClass() { - server.result().close(); - vertx.close(); - } - - @Test - public void testGet_withCallbacks() throws InterruptedException { - getCall_withCallbacks(); - // Wait for transaction to finish - InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbacks", "localhost"); - } - - @Trace(dispatcher = true) - public void getCall_withCallbacks() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - HttpClient httpClient = vertx.createHttpClient(); - - httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { - if (reqAsyncResult.succeeded()) { //Request object successfully created - HttpClientRequest request = reqAsyncResult.result(); - request.send(respAsyncResult -> { //Sending the request - if (respAsyncResult.succeeded()) { - NewRelic.addCustomParameter("responseHandler", "true"); - HttpClientResponse response = respAsyncResult.result(); - latch.countDown(); - response.body(respBufferAsyncResult -> { //Retrieve response - if (respBufferAsyncResult.succeeded()) { - NewRelic.addCustomParameter("bodyHandler", "true"); - // Handle body - } else { - // Handle server error, for example, connection closed - } - }); - } else { - // Handle server error, for example, connection closed - } - }); - } else { - // Connection error, for example, invalid server or invalid SSL certificate - } - }); - latch.await(); - } - - @Test - public void testGet_withCallbackAndFutures() throws InterruptedException { - getCall_withCallbackAndFutures(); - // Wait for transaction to finish - InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(1000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/getCall_withCallbackAndFutures", "localhost"); - } - - @Trace(dispatcher = true) - public void getCall_withCallbackAndFutures() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - HttpClient httpClient = vertx.createHttpClient(); - - httpClient.request(HttpMethod.GET, port, "localhost", "/", ar -> { - if (ar.succeeded()) { - HttpClientRequest request = ar.result(); - request.send("foo") - .onSuccess(response -> { - NewRelic.addCustomParameter("responseHandler", "true"); - NewRelic.addCustomParameter("bodyHandler", "true"); - latch.countDown(); - }).onFailure(err -> { - // - }); - } - }); - latch.await(); - } - - @Test - public void testPost() throws InterruptedException { - postCall(); - // Wait for transaction to finish - InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/postCall", "localhost"); - } - - @Trace(dispatcher = true) - private void postCall() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - HttpClient httpClient = vertx.createHttpClient(); - - httpClient.request(HttpMethod.POST, port,"localhost", "/", reqAsyncResult -> { - if (reqAsyncResult.succeeded()) { //Request object successfully created - HttpClientRequest request = reqAsyncResult.result(); - request.send(respAsyncResult -> { //Sending the request - if (respAsyncResult.succeeded()) { - NewRelic.addCustomParameter("responseHandler", "true"); - HttpClientResponse response = respAsyncResult.result(); - latch.countDown(); - response.body(respBufferAsyncResult -> { //Retrieve response - if (respBufferAsyncResult.succeeded()) { - NewRelic.addCustomParameter("bodyHandler", "true"); - // Handle body - } - }); - } - }); - } - }); - latch.await(); - } - - @Test - public void testRedirect() throws InterruptedException { - redirect(); - // Wait for transaction to finish - InstrumentationTestRunner.getIntrospector().getFinishedTransactionCount(5000); - assertExternal("OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/redirect", "localhost"); - } - - @Trace(dispatcher = true) - public void redirect() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - HttpClient httpClient = vertx.createHttpClient(); - httpClient.request(HttpMethod.GET, port,"localhost", "/", reqAsyncResult -> { - if (reqAsyncResult.succeeded()) { //Request object successfully created - HttpClientRequest request = reqAsyncResult.result(); - request.putHeader("statusCode", "301"); - request.send(respAsyncResult -> { //Sending the request - if (respAsyncResult.succeeded()) { - NewRelic.addCustomParameter("responseHandler", "true"); - HttpClientResponse response = respAsyncResult.result(); - latch.countDown(); - response.body(respBufferAsyncResult -> { //Retrieve response - if (respBufferAsyncResult.succeeded()) { - NewRelic.addCustomParameter("bodyHandler", "true"); - // Handle body - } - }); - } - }); - } - }); - latch.await(); - } - - @Test - public void testCat() throws Exception { - try (HttpTestServer httpServer = HttpServerLocator.createAndStart()) { - cat(httpServer); - - Introspector introspector = InstrumentationTestRunner.getIntrospector(); - String host = httpServer.getEndPoint().getHost(); - String txName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxClient/cat"; - assertEquals(2, introspector.getFinishedTransactionCount(250)); - Collection names = introspector.getTransactionNames(); - assertEquals(2, names.size()); - assertTrue(names.contains(httpServer.getServerTransactionName())); - assertTrue(names.contains(txName)); - - // scoped metrics - assertEquals(1, MetricsHelper.getScopedMetricCount(txName, "ExternalTransaction/" + host + "/" - + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); - assertEquals(1, MetricsHelper.getScopedMetricCount(txName, - "Java/com.nr.vertx.instrumentation.VertxClient/cat")); - - // unscoped metrics - assertEquals(1, MetricsHelper.getUnscopedMetricCount("ExternalTransaction/" + host + "/" - + httpServer.getCrossProcessId() + "/" + httpServer.getServerTransactionName())); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/" + host + "/all")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); - - // events - Collection transactionEvents = introspector.getTransactionEvents(txName); - assertEquals(1, transactionEvents.size()); - TransactionEvent transactionEvent = transactionEvents.iterator().next(); - assertEquals(1, transactionEvent.getExternalCallCount()); - assertTrue(transactionEvent.getExternalDurationInSec() > 0); - - CatHelper.verifyOneSuccessfulCat(introspector, txName); - - // external request information - Collection externalRequests = introspector.getExternalRequests(txName); - assertEquals(1, externalRequests.size()); - ExternalRequest externalRequest = externalRequests.iterator().next(); - assertEquals(1, externalRequest.getCount()); - assertEquals(host, externalRequest.getHostname()); - } - } - - @Trace(dispatcher = true) - public void cat(HttpTestServer httpServer) throws Exception { - try { - CountDownLatch latch = new CountDownLatch(1); - HttpClient httpClient = vertx.createHttpClient(); - httpClient.request(HttpMethod.GET, httpServer.getEndPoint().getPort(),httpServer.getEndPoint().getHost(), "/", reqAsyncResult -> { - if (reqAsyncResult.succeeded()) { //Request object successfully created - HttpClientRequest request = reqAsyncResult.result(); - request.putHeader(HttpTestServer.DO_CAT, "true"); - request.send(respAsyncResult -> { //Sending the request - if (respAsyncResult.succeeded()) { - NewRelic.addCustomParameter("responseHandler", "true"); - HttpClientResponse response = respAsyncResult.result(); - latch.countDown(); - response.body(respBufferAsyncResult -> { //Retrieve response - if (respBufferAsyncResult.succeeded()) { - NewRelic.addCustomParameter("bodyHandler", "true"); - // Handle body - } - }); - } - }); - } - }); - latch.await(); - } finally { - httpServer.shutdown(); - } - } - - @Test - public void testUnknownHost_withCallbacks() throws Exception { - unknownHost_withCallbacks(); - assertUnknownHostExternal(); - } - - @Trace(dispatcher = true) - private void unknownHost_withCallbacks() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - HttpClient httpClient = vertx.createHttpClient(); - httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/", ar -> { - if (ar.failed()) { - NewRelic.addCustomParameter("exceptionHandler", "true"); - latch.countDown(); - } - }); - latch.await(); - } - - @Test - public void testUnknownHost_withReturnedFuture() throws Exception { - unknownHost_withReturnedFuture(); - assertUnknownHostExternal(); - } - - @Trace(dispatcher = true) - private void unknownHost_withReturnedFuture() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - HttpClient httpClient = vertx.createHttpClient(); - - Future r = httpClient.request(HttpMethod.GET, port, "notARealHostDuderina.com", "/"); - r.onFailure(err -> { - NewRelic.addCustomParameter("exceptionHandler", "true"); - latch.countDown(); - }); - - latch.await(); - } - - private void assertUnknownHostExternal() { - Introspector introspector = InstrumentationTestRunner.getIntrospector(); - assertEquals(1, introspector.getFinishedTransactionCount(250)); - - final String txn = introspector.getTransactionNames().iterator().next(); - assertNotNull("Transaction not found", txn); - - assertEquals(1, MetricsHelper.getScopedMetricCount(txn, "External/UnknownHost/Vertx-Client/handleResponse")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/UnknownHost/Vertx-Client/handleResponse")); - - // Unknown hosts generate no external rollups - assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/allOther")); - assertEquals(0, MetricsHelper.getUnscopedMetricCount("External/all")); - - // Make sure exception handler is linked - final TransactionEvent event = introspector.getTransactionEvents(txn).iterator().next(); - assertTrue(event.getAttributes().containsKey("exceptionHandler")); - } - - private void assertExternal(String transactionName, String host) { - Introspector introspector = InstrumentationTestRunner.getIntrospector(); - Collection externalRequests = introspector.getExternalRequests(transactionName); - ExternalRequest request = externalRequests.iterator().next(); - assertEquals(host, request.getHostname()); - assertEquals("Vertx-Client", request.getLibrary()); - assertEquals("handleResponse", request.getOperation()); - Collection events = introspector.getTransactionEvents(transactionName); - TransactionEvent event = events.iterator().next(); - assertTrue(event.getAttributes().containsKey("responseHandler")); - - assertEquals(1, MetricsHelper.getScopedMetricCount(transactionName, "External/localhost/Vertx-Client/handleResponse")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/Vertx-Client/handleResponse")); - - Collection transactionEvents = introspector.getTransactionEvents(transactionName); - assertEquals(1, transactionEvents.size()); - TransactionEvent transactionEvent = transactionEvents.iterator().next(); - assertEquals(1, transactionEvent.getExternalCallCount()); - assertTrue(transactionEvent.getExternalDurationInSec() > 0); - - // external rollups - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/localhost/all")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/allOther")); - assertEquals(1, MetricsHelper.getUnscopedMetricCount("External/all")); - } - - private static int getAvailablePort() { - int port; - - try { - ServerSocket socket = new ServerSocket(0); - port = socket.getLocalPort(); - socket.close(); - } catch (IOException e) { - throw new RuntimeException("Unable to allocate ephemeral port"); - } - return port; - } -} diff --git a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java b/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java deleted file mode 100644 index f3676b8fd0..0000000000 --- a/instrumentation/vertx-core-4.3.2/src/test/java/com/nr/vertx/instrumentation/VertxFuture.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * - * * Copyright 2020 New Relic Corporation. All rights reserved. - * * SPDX-License-Identifier: Apache-2.0 - * - */ - -package com.nr.vertx.instrumentation; - -import com.newrelic.agent.introspec.InstrumentationTestConfig; -import com.newrelic.agent.introspec.InstrumentationTestRunner; -import com.newrelic.agent.introspec.Introspector; -import com.newrelic.agent.introspec.TransactionEvent; -import com.newrelic.api.agent.NewRelic; -import com.newrelic.api.agent.Trace; -import io.vertx.core.CompositeFuture; -import io.vertx.core.Future; -import io.vertx.core.Promise; -import io.vertx.core.Vertx; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.Map; -import java.util.concurrent.CountDownLatch; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -@RunWith(InstrumentationTestRunner.class) -@InstrumentationTestConfig(includePrefixes = { "io.vertx" }) -public class VertxFuture { - - @Test - public void testCompositeFuture() throws InterruptedException { - compositeFuturesAllFuturesSucceed(); - - Introspector introspector = InstrumentationTestRunner.getIntrospector(); - assertEquals(1, introspector.getFinishedTransactionCount(500)); - - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/compositeFuturesAllFuturesSucceed"; - TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); - Map attributes = txnEvent.getAttributes(); - assertTrue(attributes.containsKey("compositeFuture")); - } - - @Trace(dispatcher = true) - private void compositeFuturesAllFuturesSucceed() throws InterruptedException { - Vertx vertx = Vertx.vertx(); - CountDownLatch latch = new CountDownLatch(1); - Promise promise1 = Promise.promise(); - Promise promise2 = Promise.promise(); - - CompositeFuture.all(promise1.future(), promise2.future()).onComplete((ar -> { - if (ar.succeeded()) { - NewRelic.addCustomParameter("compositeFuture", "yes"); - } - latch.countDown(); - })); - - vertx.setTimer(1, handler -> { - promise1.complete("promise1"); - promise2.complete("promise2"); - }); - - latch.await(); - } - - @Test - public void whenFutureFails_withThrowable_txnStillCompletes() throws InterruptedException { - failFutureWithThrowable(); - - Introspector introspector = InstrumentationTestRunner.getIntrospector(); - assertEquals(1, introspector.getFinishedTransactionCount(500)); - - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithThrowable"; - TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); - Map attributes = txnEvent.getAttributes(); - assertTrue(attributes.containsKey("future")); - } - - @Test - public void whenFutureFails_withString_txnStillCompletes() throws InterruptedException { - failFutureWithString(); - - Introspector introspector = InstrumentationTestRunner.getIntrospector(); - assertEquals(1, introspector.getFinishedTransactionCount(500)); - - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/failFutureWithString"; - TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); - Map attributes = txnEvent.getAttributes(); - assertTrue(attributes.containsKey("future")); - } - - @Test - public void whenFutureCompletes_txnStillCompletes() throws InterruptedException { - completeFutureSuccessfully(); - - Introspector introspector = InstrumentationTestRunner.getIntrospector(); - assertEquals(1, introspector.getFinishedTransactionCount(500)); - - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfully"; - TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); - Map attributes = txnEvent.getAttributes(); - assertTrue(attributes.containsKey("future")); - } - - @Test - public void whenFutureCompletes_withOnCompleteRegistered_txnStillCompletes() throws InterruptedException { - completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback(); - - Introspector introspector = InstrumentationTestRunner.getIntrospector(); - assertEquals(1, introspector.getFinishedTransactionCount(500)); - - String expectedTxnName = "OtherTransaction/Custom/com.nr.vertx.instrumentation.VertxFuture/completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback"; - TransactionEvent txnEvent = introspector.getTransactionEvents(expectedTxnName).iterator().next(); - Map attributes = txnEvent.getAttributes(); - assertTrue(attributes.containsKey("future")); - } - - @Trace(dispatcher = true) - private void failFutureWithString() throws InterruptedException { - CountDownLatch countDownLatch = new CountDownLatch(1); - - Vertx vertx = Vertx.vertx(); - Promise promise = Promise.promise(); - - Future future = promise.future(); - future.onFailure(ar -> { - NewRelic.addCustomParameter("future", "failed"); - countDownLatch.countDown(); - }); - - vertx.setTimer(1, handler -> { - promise.fail("oops"); - }); - - countDownLatch.await(); - } - - @Trace(dispatcher = true) - private void failFutureWithThrowable() throws InterruptedException { - CountDownLatch countDownLatch = new CountDownLatch(1); - - Vertx vertx = Vertx.vertx(); - Promise promise = Promise.promise(); - - Future future = promise.future(); - future.onFailure(ar -> { - NewRelic.addCustomParameter("future", "failed"); - countDownLatch.countDown(); - }); - - vertx.setTimer(1, handler -> { - promise.fail(new IllegalArgumentException("foo")); - }); - - countDownLatch.await(); - } - - @Trace(dispatcher = true) - private void completeFutureSuccessfully() throws InterruptedException { - CountDownLatch countDownLatch = new CountDownLatch(1); - - Vertx vertx = Vertx.vertx(); - Promise promise = Promise.promise(); - - Future future = promise.future(); - future.onSuccess(ar -> { - NewRelic.addCustomParameter("future", "success"); - countDownLatch.countDown(); - }); - - vertx.setTimer(1, handler -> { - promise.complete("hooray"); - }); - - - countDownLatch.await(); - } - - @Trace(dispatcher = true) - private void completeFutureSuccessfullyOnlyRegisteringOnCompleteCallback() throws InterruptedException { - CountDownLatch countDownLatch = new CountDownLatch(1); - - Vertx vertx = Vertx.vertx(); - Promise promise = Promise.promise(); - - Future future = promise.future(); - future.onComplete(ar -> { - NewRelic.addCustomParameter("future", "onComplete"); - countDownLatch.countDown(); - }); - - vertx.setTimer(1, handler -> { - promise.complete("hooray"); - }); - - countDownLatch.await(); - } -} From ded5fdbaff9d9ceb9cc4dea4f59ee0f8d2d64603 Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Mon, 13 Nov 2023 14:45:43 -0500 Subject: [PATCH 13/16] Remove unneeded .gitignore --- instrumentation/vertx-core-4.0.0/.gitignore | 42 --------------------- instrumentation/vertx-core-4.1.0/.gitignore | 42 --------------------- instrumentation/vertx-core-4.3.2/.gitignore | 42 --------------------- 3 files changed, 126 deletions(-) delete mode 100644 instrumentation/vertx-core-4.0.0/.gitignore delete mode 100644 instrumentation/vertx-core-4.1.0/.gitignore delete mode 100644 instrumentation/vertx-core-4.3.2/.gitignore diff --git a/instrumentation/vertx-core-4.0.0/.gitignore b/instrumentation/vertx-core-4.0.0/.gitignore deleted file mode 100644 index b63da4551b..0000000000 --- a/instrumentation/vertx-core-4.0.0/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store \ No newline at end of file diff --git a/instrumentation/vertx-core-4.1.0/.gitignore b/instrumentation/vertx-core-4.1.0/.gitignore deleted file mode 100644 index b63da4551b..0000000000 --- a/instrumentation/vertx-core-4.1.0/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store \ No newline at end of file diff --git a/instrumentation/vertx-core-4.3.2/.gitignore b/instrumentation/vertx-core-4.3.2/.gitignore deleted file mode 100644 index b63da4551b..0000000000 --- a/instrumentation/vertx-core-4.3.2/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store \ No newline at end of file From 8e5ff8ccfb16a6e767f4406fd36422ba835128de Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Wed, 29 Nov 2023 15:11:48 -0500 Subject: [PATCH 14/16] Address PR comments; correct token link error in HttpClientRequestPromiseWrapper --- .../HttpClientRequestPromiseWrapper.java | 11 ++++------- .../http/impl/HttpClientImpl_Instrumentation.java | 7 +++++++ .../nr/vertx/instrumentation/VertxClientTest.java | 2 +- .../nr/vertx/instrumentation/VertxFutureTest.java | 2 +- .../HttpClientRequestPromiseWrapper.java | 11 ++++------- .../nr/vertx/instrumentation/VertxCoreUtil.java | 6 +++--- .../http/impl/HttpClientImpl_Instrumentation.java | 7 +++++++ .../HttpClientRequestImpl_Instrumentation.java | 1 - .../nr/vertx/instrumentation/VertxClientTest.java | 2 +- instrumentation/vertx-core-4.3.2/build.gradle | 3 --- .../HttpClientRequestPromiseWrapper.java | 11 ++++------- .../http/impl/HttpClientImpl_Instrumentation.java | 3 --- .../HttpClientRequestBase_Instrumentation.java | 14 -------------- 13 files changed, 32 insertions(+), 48 deletions(-) diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java index f567a17e84..5ab6cd229b 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java @@ -48,18 +48,15 @@ public void complete(HttpClientRequest req) { @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryFail(Throwable t) { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Transaction txn = AgentBridge.getAgent().getTransaction(false); if (t.toString().contains("UnknownHostException") && txn != null) { Segment segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); VertxCoreUtil.reportUnknownHost(segment); - final Token token = segment.getTransaction().getToken(); segment.end(); - token.linkAndExpire(); + if (token != null) { + token.linkAndExpire(); + token = null; + } AgentBridge.getAgent().getTransaction(false).expireAllTokens(); } diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java index e5288565ac..3c11209193 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java @@ -1,3 +1,10 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + package io.vertx.core.http.impl; import com.newrelic.api.agent.NewRelic; diff --git a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java index fb9ed1d527..9665151125 100644 --- a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java +++ b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ diff --git a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java index 5cc5440ce3..87ab208439 100644 --- a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java +++ b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxFutureTest.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java index 4c577f7fe0..16ca358415 100644 --- a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java @@ -48,18 +48,15 @@ public void complete(HttpClientRequest req) { @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryFail(Throwable t) { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Transaction txn = AgentBridge.getAgent().getTransaction(false); if (t.toString().contains("UnknownHostException") && txn != null) { Segment segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); VertxCoreUtil.reportUnknownHost(segment); - final Token token = segment.getTransaction().getToken(); segment.end(); - token.linkAndExpire(); + if (token != null) { + token.linkAndExpire(); + token = null; + } AgentBridge.getAgent().getTransaction(false).expireAllTokens(); } diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java index cb98790edb..599cc3ea3c 100644 --- a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/VertxCoreUtil.java @@ -41,9 +41,9 @@ public static void storeToken(Listener handler) { } } - public static void linkAndExpireToken(Listener handler) { - if (handler != null) { - final Token token = tokenMap.remove(handler); + public static void linkAndExpireToken(Listener listener) { + if (listener != null) { + final Token token = tokenMap.remove(listener); if (token != null) { token.linkAndExpire(); } diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java index 9b7eff5baf..f91cf11f10 100644 --- a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java @@ -1,3 +1,10 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + package io.vertx.core.http.impl; import com.newrelic.api.agent.NewRelic; diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java index 8265c3d713..fb630fde79 100644 --- a/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.1.0/src/main/java/io/vertx/core/http/impl/HttpClientRequestImpl_Instrumentation.java @@ -27,7 +27,6 @@ public abstract class HttpClientRequestImpl_Instrumentation extends HttpClientRe public Future end(Buffer chunk) { if (AgentBridge.getAgent().getTransaction(false) != null) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in end(Buffer chunk 2"); segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); } diff --git a/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java index fb9ed1d527..9665151125 100644 --- a/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java +++ b/instrumentation/vertx-core-4.1.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java @@ -1,6 +1,6 @@ /* * - * * Copyright 2020 New Relic Corporation. All rights reserved. + * * Copyright 2023 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ diff --git a/instrumentation/vertx-core-4.3.2/build.gradle b/instrumentation/vertx-core-4.3.2/build.gradle index b520cba843..a4c5ad7748 100644 --- a/instrumentation/vertx-core-4.3.2/build.gradle +++ b/instrumentation/vertx-core-4.3.2/build.gradle @@ -14,9 +14,6 @@ dependencies { verifyInstrumentation { passesOnly 'io.vertx:vertx-core:[4.3.2,)' - excludeRegex '.*CR[0-9]*' - excludeRegex '.*-milestone[0-9]' - excludeRegex '.*Beta[0-9]' } site { diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java index 4c577f7fe0..16ca358415 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java @@ -48,18 +48,15 @@ public void complete(HttpClientRequest req) { @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryFail(Throwable t) { - if (token != null) { - token.linkAndExpire(); - token = null; - } - Transaction txn = AgentBridge.getAgent().getTransaction(false); if (t.toString().contains("UnknownHostException") && txn != null) { Segment segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); VertxCoreUtil.reportUnknownHost(segment); - final Token token = segment.getTransaction().getToken(); segment.end(); - token.linkAndExpire(); + if (token != null) { + token.linkAndExpire(); + token = null; + } AgentBridge.getAgent().getTransaction(false).expireAllTokens(); } diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java index efed450c92..05373f3f62 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java @@ -5,12 +5,9 @@ import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.Weaver; import com.nr.vertx.instrumentation.HttpClientRequestPromiseWrapper; -import io.vertx.core.MultiMap; import io.vertx.core.http.HttpClientRequest; -import io.vertx.core.http.HttpMethod; import io.vertx.core.http.RequestOptions; import io.vertx.core.impl.future.PromiseInternal; -import io.vertx.core.net.SocketAddress; @Weave(type = MatchType.BaseClass, originalName = "io.vertx.core.http.impl.HttpClientImpl") public class HttpClientImpl_Instrumentation { diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java index 76e3c8225e..427bcc6137 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -31,20 +31,6 @@ public abstract class HttpClientRequestBase_Instrumentation { public abstract MultiMap headers(); -// @Trace(async = true) -// public HttpClientRequest response(Handler> handler) { -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response " + handler); -// if (AgentBridge.getAgent().getTransaction(false) != null) { -// this.token = NewRelic.getAgent().getTransaction().getToken(); -// System.out.println("agent txn: " + NewRelic.getAgent().getTransaction()); -// AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in HttpClientRequest response"); -// segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); -// segment.addOutboundRequestHeaders(new OutboundWrapper(headers())); -// } -// -// return Weaver.callOriginal(); -// } - @Trace(async = true) void handleResponse(Promise promise, HttpClientResponse resp, long timeoutMs) { AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse()"); From b65d6bf53e9098ba7e839decb0e6e66b8f6db85b Mon Sep 17 00:00:00 2001 From: Andre Onuki Date: Wed, 29 Nov 2023 16:29:53 -0500 Subject: [PATCH 15/16] Removing debug logging --- .../core/http/impl/HttpClientImpl_Instrumentation.java | 7 +++++++ .../http/impl/HttpClientRequestBase_Instrumentation.java | 4 ---- .../src/main/java/com/newrelic/agent/Segment.java | 1 - .../main/java/com/newrelic/agent/TransactionApiImpl.java | 2 -- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java index 05373f3f62..6b7bfd8de7 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientImpl_Instrumentation.java @@ -1,3 +1,10 @@ +/* + * + * * Copyright 2023 New Relic Corporation. All rights reserved. + * * SPDX-License-Identifier: Apache-2.0 + * + */ + package io.vertx.core.http.impl; import com.newrelic.api.agent.NewRelic; diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java index 427bcc6137..4350c8183d 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/io/vertx/core/http/impl/HttpClientRequestBase_Instrumentation.java @@ -33,10 +33,7 @@ public abstract class HttpClientRequestBase_Instrumentation { @Trace(async = true) void handleResponse(Promise promise, HttpClientResponse resp, long timeoutMs) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse()"); if (segment != null) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleResponse() segment != null && this.token != null"); - final Token segmentToken = segment.getTransaction().getToken(); reportExternal(resp, segment); segment.end(); @@ -49,7 +46,6 @@ void handleResponse(Promise promise, HttpClientResponse resp @Trace(async = true) void handleException(Throwable t) { - AgentBridge.getAgent().getLogger().log(Level.INFO, "vertx4 in handleException()"); if (segment != null) { if (t instanceof UnknownHostException) { VertxCoreUtil.reportUnknownHost(segment); diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/Segment.java b/newrelic-agent/src/main/java/com/newrelic/agent/Segment.java index a4116f02bb..4c25838c43 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/Segment.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/Segment.java @@ -187,7 +187,6 @@ public void finish(final Throwable t) { private void finish(final Throwable t, boolean async) { if (!isFinished.getAndSet(true)) { - System.out.println("finish " + this); markFinishTime(); diff --git a/newrelic-agent/src/main/java/com/newrelic/agent/TransactionApiImpl.java b/newrelic-agent/src/main/java/com/newrelic/agent/TransactionApiImpl.java index c5054a3494..292091654c 100644 --- a/newrelic-agent/src/main/java/com/newrelic/agent/TransactionApiImpl.java +++ b/newrelic-agent/src/main/java/com/newrelic/agent/TransactionApiImpl.java @@ -469,8 +469,6 @@ public Segment startSegment(String category, String segmentName) { Segment segment = tx.startSegment(category, segmentName); - System.out.println("startSegment " + segment); - return segment == null ? NoOpSegment.INSTANCE : segment; } From 28558e490a2ccf32b9d465e6970d43ee964277db Mon Sep 17 00:00:00 2001 From: Jerry Duffy Date: Thu, 30 Nov 2023 08:52:47 -0500 Subject: [PATCH 16/16] Fix unit tests --- .../instrumentation/HttpClientRequestPromiseWrapper.java | 5 ++++- .../java/com/nr/vertx/instrumentation/VertxClientTest.java | 2 +- .../instrumentation/HttpClientRequestPromiseWrapper.java | 5 ++++- .../instrumentation/HttpClientRequestPromiseWrapper.java | 5 ++++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java index 5ab6cd229b..e640ab3056 100644 --- a/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java +++ b/instrumentation/vertx-core-4.0.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java @@ -48,13 +48,16 @@ public void complete(HttpClientRequest req) { @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryFail(Throwable t) { + if (token != null) { + token.link(); + } Transaction txn = AgentBridge.getAgent().getTransaction(false); if (t.toString().contains("UnknownHostException") && txn != null) { Segment segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); VertxCoreUtil.reportUnknownHost(segment); segment.end(); if (token != null) { - token.linkAndExpire(); + token.expire(); token = null; } AgentBridge.getAgent().getTransaction(false).expireAllTokens(); diff --git a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java index 9665151125..90e96a4e86 100644 --- a/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java +++ b/instrumentation/vertx-core-4.0.0/src/test/java/com/nr/vertx/instrumentation/VertxClientTest.java @@ -327,7 +327,7 @@ private void unknownHost_withReturnedFuture() throws InterruptedException { private void assertUnknownHostExternal() { Introspector introspector = InstrumentationTestRunner.getIntrospector(); - assertEquals(1, introspector.getFinishedTransactionCount(250)); + assertEquals(1, introspector.getFinishedTransactionCount(1000)); final String txn = introspector.getTransactionNames().iterator().next(); assertNotNull("Transaction not found", txn); diff --git a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java index 16ca358415..5d0fbe13f0 100644 --- a/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java +++ b/instrumentation/vertx-core-4.1.0/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java @@ -48,13 +48,16 @@ public void complete(HttpClientRequest req) { @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryFail(Throwable t) { + if (token != null) { + token.link(); + } Transaction txn = AgentBridge.getAgent().getTransaction(false); if (t.toString().contains("UnknownHostException") && txn != null) { Segment segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); VertxCoreUtil.reportUnknownHost(segment); segment.end(); if (token != null) { - token.linkAndExpire(); + token.expire(); token = null; } AgentBridge.getAgent().getTransaction(false).expireAllTokens(); diff --git a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java index 16ca358415..5d0fbe13f0 100644 --- a/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java +++ b/instrumentation/vertx-core-4.3.2/src/main/java/com/nr/vertx/instrumentation/HttpClientRequestPromiseWrapper.java @@ -48,13 +48,16 @@ public void complete(HttpClientRequest req) { @Trace(async = true, excludeFromTransactionTrace = true) public boolean tryFail(Throwable t) { + if (token != null) { + token.link(); + } Transaction txn = AgentBridge.getAgent().getTransaction(false); if (t.toString().contains("UnknownHostException") && txn != null) { Segment segment = NewRelic.getAgent().getTransaction().startSegment(VERTX_CLIENT, END); VertxCoreUtil.reportUnknownHost(segment); segment.end(); if (token != null) { - token.linkAndExpire(); + token.expire(); token = null; } AgentBridge.getAgent().getTransaction(false).expireAllTokens();