Skip to content

Commit

Permalink
Merge pull request #1588 from newrelic/vertx4-module
Browse files Browse the repository at this point in the history
Vert.x v4.x instrumentation module
  • Loading branch information
jtduffy authored Nov 30, 2023
2 parents 67b120f + 28558e4 commit 760bbbe
Show file tree
Hide file tree
Showing 58 changed files with 3,938 additions and 6 deletions.
2 changes: 1 addition & 1 deletion instrumentation/vertx-core-3.3.0/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion instrumentation/vertx-core-3.3.3/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion instrumentation/vertx-core-3.4.1/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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]*'
}

Expand Down
2 changes: 1 addition & 1 deletion instrumentation/vertx-core-3.6.0/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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]*'
}

Expand Down
2 changes: 1 addition & 1 deletion instrumentation/vertx-core-3.8.0/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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]'
}
Expand Down
2 changes: 1 addition & 1 deletion instrumentation/vertx-core-3.9.0/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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]'
}
Expand Down
42 changes: 42 additions & 0 deletions instrumentation/vertx-core-4.0.0/README.md
Original file line number Diff line number Diff line change
@@ -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
});
}
});
```
25 changes: 25 additions & 0 deletions instrumentation/vertx-core-4.0.0/build.gradle
Original file line number Diff line number Diff line change
@@ -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.1.0)'
excludeRegex '.*CR[0-9]*'
excludeRegex '.*-milestone[0-9]'
excludeRegex '.*Beta[0-9]'
}

site {
title 'Vertx'
type 'Framework'
}
Original file line number Diff line number Diff line change
@@ -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<T> implements Handler<AsyncResult<T>> {
private Handler<AsyncResult<T>> original;

private Token token;

public AsyncHandlerWrapper(Handler<AsyncResult<T>> original, Token token) {
this.original = original;
this.token = token;
}

@Override
@Trace(async = true, excludeFromTransactionTrace = true)
public void handle(AsyncResult<T> event) {
if (token != null) {
token.linkAndExpire();
token = null;
}

this.original.handle(event);
this.original = null;
}
}
Original file line number Diff line number Diff line change
@@ -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<HttpClientRequest> {

private final PromiseInternal<HttpClientRequest> original;

private Token token;

public HttpClientRequestPromiseWrapper(PromiseInternal<HttpClientRequest> 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.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.expire();
token = null;
}
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<HttpClientRequest> future() {
return original.future();
}

public ContextInternal context() {
return original.context();
}

@Override
public void addListener(Listener<HttpClientRequest> listener) {
original.addListener(listener);
}

@Override
public void operationComplete(Future<HttpClientRequest> future) throws Exception {
original.operationComplete(future);
}

@Override
public boolean isComplete() {
return original.isComplete();
}

@Override
public io.vertx.core.Future<HttpClientRequest> onComplete(Handler<AsyncResult<HttpClientRequest>> 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 <U> io.vertx.core.Future<U> compose(Function<HttpClientRequest, io.vertx.core.Future<U>> successMapper,
Function<Throwable, io.vertx.core.Future<U>> failureMapper) {
return original.compose(successMapper, failureMapper);
}

@Override
public <U> io.vertx.core.Future<U> eventually(Function<Void, io.vertx.core.Future<U>> mapper) {
return original.eventually(mapper);
}

@Override
public <U> io.vertx.core.Future<U> map(Function<HttpClientRequest, U> mapper) {
return original.map(mapper);
}

@Override
public <V> io.vertx.core.Future<V> map(V value) {
return original.map(value);
}

@Override
public io.vertx.core.Future<HttpClientRequest> otherwise(Function<Throwable, HttpClientRequest> mapper) {
return original.otherwise(mapper);
}

@Override
public io.vertx.core.Future<HttpClientRequest> otherwise(HttpClientRequest value) {
return original.otherwise(value);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Loading

0 comments on commit 760bbbe

Please sign in to comment.