Skip to content

Commit 763d13c

Browse files
authored
Merge pull request #630 from commercetools/newrelic_add_metrics
Newrelic add metrics
2 parents 8c1cc19 + 5fd5de3 commit 763d13c

File tree

6 files changed

+104
-3
lines changed

6 files changed

+104
-3
lines changed

.git-blame-ignore-revs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
a8ec45c8ea4ba559247b654d01b0d35b21a68865
22
33f3224cb40e3fa8c56ddb88962e3a4e9319685d
33
430a1a0a5dd4efe78e21526c37bec9dbce036401
4-
d0129c1095216d5c830900c8a6223ef5d4274de1
4+
d0129c1095216d5c830900c8a6223ef5d4274de1
5+
4bc5c823b8ebf5a00491c7e63e1ea49d29bf5ee7

commercetools/commercetools-monitoring-newrelic/src/main/java/com/commercetools/monitoring/newrelic/NewRelicTelemetryMiddleware.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11

22
package com.commercetools.monitoring.newrelic;
33

4+
import static com.commercetools.monitoring.newrelic.NewrelicInfo.*;
5+
6+
import java.time.Duration;
7+
import java.time.Instant;
48
import java.util.Optional;
59
import java.util.concurrent.CompletableFuture;
610
import java.util.function.Function;
@@ -19,6 +23,18 @@
1923
*
2024
* {@include.example example.NewRelicApiRootBuilderTest#addNewRelic()}
2125
*
26+
* The middleware adds the following metrics to Newrelic:
27+
* <ul>
28+
* <li>Custom/Commercetools/Client/Duration: The duration of the request in milliseconds</li>
29+
* <li>Custom/Commercetools/Client/Request/Total: The total number of requests</li>
30+
* <li>Custom/Commercetools/Client/Request/Error: The total number of requests with a status code greater or equal to 400</li>
31+
* </ul>
32+
*
33+
* <p>The metrics are added as metric timeslice data, therefore an APM is expected in the application.</p>
34+
*
35+
* <br/>
36+
* <h2>Implementation details</h2>
37+
*
2238
* <p>The middleware reads the {@link NewRelicContext} from the Request and restores the transaction using a {@link Token}
2339
* The details of the request and response are then reported as {@link Segment} with {@link HttpParameters}</p>
2440
*
@@ -39,7 +55,7 @@ public class NewRelicTelemetryMiddleware implements TelemetryMiddleware {
3955
@Override
4056
public CompletableFuture<ApiHttpResponse<byte[]>> invoke(ApiHttpRequest request,
4157
Function<ApiHttpRequest, CompletableFuture<ApiHttpResponse<byte[]>>> next) {
42-
58+
final Instant start = Instant.now();
4359
Optional<NewRelicContext> context = Optional.ofNullable(request.getContext(NewRelicContext.class));
4460
context.map(NewRelicContext::getToken).ifPresent(Token::link);
4561
Optional<Token> token = context.map(NewRelicContext::getTransaction).map(Transaction::getToken);
@@ -54,6 +70,14 @@ public CompletableFuture<ApiHttpResponse<byte[]>> invoke(ApiHttpRequest request,
5470
.status(response.getStatusCode(), response.getMessage())
5571
.build()));
5672
segment.ifPresent(Segment::end);
73+
74+
NewRelic.incrementCounter(PREFIX + CLIENT_REQUEST_TOTAL);
75+
NewRelic.recordResponseTimeMetric(PREFIX + CLIENT_DURATION,
76+
Duration.between(start, Instant.now()).toMillis());
77+
78+
if (response.getStatusCode() >= 400) {
79+
NewRelic.incrementCounter(PREFIX + CLIENT_REQUEST_ERROR);
80+
}
5781
return response;
5882
});
5983
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
package com.commercetools.monitoring.newrelic;
3+
4+
public class NewrelicInfo {
5+
public static final String CLIENT_DURATION = "Client/Duration";
6+
public static final String CLIENT_REQUEST_ERROR = "Client/Request/Error";
7+
public static final String CLIENT_REQUEST_TOTAL = "Client/Request/Total";
8+
public static final String JSON_SERIALIZATION = "Json/Serialization";
9+
public static final String JSON_DESERIALIZATION = "Json/Deserialization";
10+
public static final String PREFIX = "Custom/Commercetools/";
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
2+
package com.commercetools.monitoring.newrelic;
3+
4+
import static com.commercetools.monitoring.newrelic.NewrelicInfo.*;
5+
6+
import java.time.Duration;
7+
import java.time.Instant;
8+
9+
import com.fasterxml.jackson.core.JsonProcessingException;
10+
import com.fasterxml.jackson.core.type.TypeReference;
11+
import com.fasterxml.jackson.databind.JavaType;
12+
import com.newrelic.api.agent.NewRelic;
13+
14+
import io.vrap.rmf.base.client.ApiHttpResponse;
15+
import io.vrap.rmf.base.client.ResponseSerializer;
16+
17+
/**
18+
* This serializer uses API to submit metrics to newrelic.
19+
*/
20+
public class NewrelicResponseSerializer implements ResponseSerializer {
21+
private final ResponseSerializer serializer;
22+
23+
public NewrelicResponseSerializer(final ResponseSerializer serializer) {
24+
this.serializer = serializer;
25+
}
26+
27+
@Override
28+
public <O> ApiHttpResponse<O> convertResponse(ApiHttpResponse<byte[]> response, Class<O> outputType) {
29+
Instant start = Instant.now();
30+
ApiHttpResponse<O> result = serializer.convertResponse(response, outputType);
31+
long durationInMillis = Duration.between(start, Instant.now()).toMillis();
32+
NewRelic.recordResponseTimeMetric(PREFIX + JSON_SERIALIZATION, durationInMillis);
33+
return result;
34+
}
35+
36+
@Override
37+
public <O> ApiHttpResponse<O> convertResponse(ApiHttpResponse<byte[]> response, JavaType outputType) {
38+
Instant start = Instant.now();
39+
ApiHttpResponse<O> result = serializer.convertResponse(response, outputType);
40+
long durationInMillis = Duration.between(start, Instant.now()).toMillis();
41+
NewRelic.recordResponseTimeMetric(PREFIX + JSON_SERIALIZATION, durationInMillis);
42+
return result;
43+
}
44+
45+
@Override
46+
public <O> ApiHttpResponse<O> convertResponse(ApiHttpResponse<byte[]> response, TypeReference<O> outputType) {
47+
Instant start = Instant.now();
48+
ApiHttpResponse<O> result = serializer.convertResponse(response, outputType);
49+
long durationInMillis = Duration.between(start, Instant.now()).toMillis();
50+
NewRelic.recordResponseTimeMetric(PREFIX + JSON_SERIALIZATION, durationInMillis);
51+
return result;
52+
}
53+
54+
@Override
55+
public byte[] toJsonByteArray(Object value) throws JsonProcessingException {
56+
Instant start = Instant.now();
57+
byte[] result = serializer.toJsonByteArray(value);
58+
long durationInMillis = Duration.between(start, Instant.now()).toMillis();
59+
NewRelic.recordResponseTimeMetric(PREFIX + JSON_DESERIALIZATION, durationInMillis);
60+
return result;
61+
}
62+
63+
}

examples/spring-newrelic/src/main/java/com/commercetools/sdk/examples/springmvc/service/CtpClientBeanService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import com.commercetools.monitoring.newrelic.NewRelicContext;
99
import com.commercetools.monitoring.newrelic.NewRelicTelemetryMiddleware;
10+
import com.commercetools.monitoring.newrelic.NewrelicResponseSerializer;
1011
import com.newrelic.api.agent.NewRelic;
1112
import com.newrelic.api.agent.Trace;
1213
import io.vrap.rmf.base.client.*;
@@ -40,6 +41,7 @@ public ApiHttpClient client() {
4041
return ApiRootBuilder.of()
4142
.defaultClient(credentials())
4243
.withTelemetryMiddleware(new NewRelicTelemetryMiddleware())
44+
.withSerializer(new NewrelicResponseSerializer(ResponseSerializer.of()))
4345
.buildClient();
4446
}
4547

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import java.util.concurrent.ExecutionException;
2121

2222
@Controller
23-
public class AppContoller {
23+
public class AppController {
2424
@GetMapping("/")
2525
public String home() {
2626
return "home/index";

0 commit comments

Comments
 (0)