Skip to content

Commit

Permalink
Add ResponseMeteredLevel parameter to ResponseMetered for Jersey 3.1.x (
Browse files Browse the repository at this point in the history
#3116)

Adds a parameter ResponseMeteredLevel to the ResponseMetered annotation which decides whether 1xx/2xx/3xx/4xx/5xx meters, individual response code meters or both are created.

Port of #3043
  • Loading branch information
dennyac committed Feb 7, 2023
1 parent ebe410d commit a97b0bb
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 52 deletions.
Expand Up @@ -9,6 +9,7 @@
import com.codahale.metrics.annotation.ExceptionMetered;
import com.codahale.metrics.annotation.Metered;
import com.codahale.metrics.annotation.ResponseMetered;
import com.codahale.metrics.annotation.ResponseMeteredLevel;
import com.codahale.metrics.annotation.Timed;
import jakarta.ws.rs.core.Configuration;
import jakarta.ws.rs.ext.Provider;
Expand All @@ -26,13 +27,19 @@
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import static com.codahale.metrics.MetricRegistry.name;
import static com.codahale.metrics.annotation.ResponseMeteredLevel.ALL;
import static com.codahale.metrics.annotation.ResponseMeteredLevel.COARSE;
import static com.codahale.metrics.annotation.ResponseMeteredLevel.DETAILED;

/**
* An application event listener that listens for Jersey application initialization to
Expand Down Expand Up @@ -126,19 +133,48 @@ public ExceptionMeterMetric(final MetricRegistry registry,
* different response codes
*/
private static class ResponseMeterMetric {
public final List<Meter> meters;
private static final Set<ResponseMeteredLevel> COARSE_METER_LEVELS = EnumSet.of(COARSE, ALL);
private static final Set<ResponseMeteredLevel> DETAILED_METER_LEVELS = EnumSet.of(DETAILED, ALL);
private final List<Meter> meters;
private final Map<Integer, Meter> responseCodeMeters;
private final MetricRegistry metricRegistry;
private final String metricName;
private final ResponseMeteredLevel level;

public ResponseMeterMetric(final MetricRegistry registry,
final ResourceMethod method,
final ResponseMetered responseMetered) {
final String metricName = chooseName(responseMetered.name(), responseMetered.absolute(), method);
this.meters = Collections.unmodifiableList(Arrays.asList(
registry.meter(name(metricName, "1xx-responses")), // 1xx
registry.meter(name(metricName, "2xx-responses")), // 2xx
registry.meter(name(metricName, "3xx-responses")), // 3xx
registry.meter(name(metricName, "4xx-responses")), // 4xx
registry.meter(name(metricName, "5xx-responses")) // 5xx
));
this.metricName = chooseName(responseMetered.name(), responseMetered.absolute(), method);
this.level = responseMetered.level();
this.meters = COARSE_METER_LEVELS.contains(level) ?
Collections.unmodifiableList(Arrays.asList(
registry.meter(name(metricName, "1xx-responses")), // 1xx
registry.meter(name(metricName, "2xx-responses")), // 2xx
registry.meter(name(metricName, "3xx-responses")), // 3xx
registry.meter(name(metricName, "4xx-responses")), // 4xx
registry.meter(name(metricName, "5xx-responses")) // 5xx
)) : Collections.emptyList();
this.responseCodeMeters = DETAILED_METER_LEVELS.contains(level) ? new ConcurrentHashMap<>() : Collections.emptyMap();
this.metricRegistry = registry;
}

public void mark(int statusCode) {
if (DETAILED_METER_LEVELS.contains(level)) {
getResponseCodeMeter(statusCode).mark();
}

if (COARSE_METER_LEVELS.contains(level)) {
final int responseStatus = statusCode / 100;
if (responseStatus >= 1 && responseStatus <= 5) {
meters.get(responseStatus - 1).mark();
}
}
}

private Meter getResponseCodeMeter(int statusCode) {
return responseCodeMeters
.computeIfAbsent(statusCode, sc -> metricRegistry
.meter(name(metricName, String.format("%d-responses", sc))));
}
}

Expand Down Expand Up @@ -270,15 +306,10 @@ public void onEvent(RequestEvent event) {

if (metric != null) {
ContainerResponse containerResponse = event.getContainerResponse();
if (containerResponse == null) {
if (event.getException() != null) {
metric.meters.get(4).mark();
}
if (containerResponse == null && event.getException() != null) {
metric.mark(500);
} else {
final int responseStatus = containerResponse.getStatus() / 100;
if (responseStatus >= 1 && responseStatus <= 5) {
metric.meters.get(responseStatus - 1).mark();
}
metric.mark(containerResponse.getStatus());
}
}
}
Expand Down
Expand Up @@ -95,38 +95,73 @@ public void exceptionMeteredMethodsAreExceptionMetered() {
}

@Test
public void responseMeteredMethodsAreMetered() {
public void responseMeteredMethodsAreMeteredWithCoarseLevel() {
final Meter meter2xx = registry.meter(name(InstrumentedResource.class,
"response2xxMetered",
"responseMeteredCoarse",
"2xx-responses"));
final Meter meter4xx = registry.meter(name(InstrumentedResource.class,
"response4xxMetered",
"4xx-responses"));
final Meter meter5xx = registry.meter(name(InstrumentedResource.class,
"response5xxMetered",
"5xx-responses"));
final Meter meter200 = registry.meter(name(InstrumentedResource.class,
"responseMeteredCoarse",
"200-responses"));

assertThat(meter2xx.getCount()).isZero();
assertThat(target("response-2xx-metered")
assertThat(meter200.getCount()).isZero();
assertThat(target("response-metered-coarse")
.request()
.get().getStatus())
.isEqualTo(200);

assertThat(meter4xx.getCount()).isZero();
assertThat(target("response-4xx-metered")
assertThat(meter2xx.getCount()).isOne();
assertThat(meter200.getCount()).isZero();
}

@Test
public void responseMeteredMethodsAreMeteredWithDetailedLevel() {
final Meter meter2xx = registry.meter(name(InstrumentedResource.class,
"responseMeteredDetailed",
"2xx-responses"));
final Meter meter200 = registry.meter(name(InstrumentedResource.class,
"responseMeteredDetailed",
"200-responses"));
final Meter meter201 = registry.meter(name(InstrumentedResource.class,
"responseMeteredDetailed",
"201-responses"));

assertThat(meter2xx.getCount()).isZero();
assertThat(meter200.getCount()).isZero();
assertThat(meter201.getCount()).isZero();
assertThat(target("response-metered-detailed")
.request()
.get().getStatus())
.isEqualTo(200);
assertThat(target("response-metered-detailed")
.queryParam("status_code", 201)
.request()
.get().getStatus())
.isEqualTo(400);
.isEqualTo(201);

assertThat(meter5xx.getCount()).isZero();
assertThat(target("response-5xx-metered")
assertThat(meter2xx.getCount()).isZero();
assertThat(meter200.getCount()).isOne();
assertThat(meter201.getCount()).isOne();
}

@Test
public void responseMeteredMethodsAreMeteredWithAllLevel() {
final Meter meter2xx = registry.meter(name(InstrumentedResource.class,
"responseMeteredAll",
"2xx-responses"));
final Meter meter200 = registry.meter(name(InstrumentedResource.class,
"responseMeteredAll",
"200-responses"));

assertThat(meter2xx.getCount()).isZero();
assertThat(meter200.getCount()).isZero();
assertThat(target("response-metered-all")
.request()
.get().getStatus())
.isEqualTo(500);
.isEqualTo(200);

assertThat(meter2xx.getCount()).isEqualTo(1);
assertThat(meter4xx.getCount()).isEqualTo(1);
assertThat(meter5xx.getCount()).isEqualTo(1);
assertThat(meter2xx.getCount()).isOne();
assertThat(meter200.getCount()).isOne();
}

@Test
Expand Down
Expand Up @@ -126,14 +126,21 @@ public void responseMeteredUnmappedExceptionPerClassMethodsAreMetered() {

@Test
public void subresourcesFromLocatorsRegisterMetrics() {
final Meter meter2xx = registry.meter(name(InstrumentedSubResourceResponseMeteredPerClass.class,
"responseMeteredPerClass",
"2xx-responses"));
final Meter meter200 = registry.meter(name(InstrumentedSubResourceResponseMeteredPerClass.class,
"responseMeteredPerClass",
"200-responses"));

assertThat(meter2xx.getCount()).isZero();
assertThat(meter200.getCount()).isZero();
assertThat(target("subresource/responseMeteredPerClass")
.request()
.get().getStatus())
.isEqualTo(200);

final Meter meter = registry.meter(name(InstrumentedSubResourceResponseMeteredPerClass.class,
"responseMeteredPerClass",
"2xx-responses"));
assertThat(meter.getCount()).isEqualTo(1);
assertThat(meter2xx.getCount()).isOne();
assertThat(meter200.getCount()).isOne();
}
}
Expand Up @@ -14,6 +14,10 @@

import java.io.IOException;

import static com.codahale.metrics.annotation.ResponseMeteredLevel.ALL;
import static com.codahale.metrics.annotation.ResponseMeteredLevel.COARSE;
import static com.codahale.metrics.annotation.ResponseMeteredLevel.DETAILED;

@Path("/")
@Produces(MediaType.TEXT_PLAIN)
public class InstrumentedResource {
Expand Down Expand Up @@ -42,24 +46,24 @@ public String exceptionMetered(@QueryParam("splode") @DefaultValue("false") bool
}

@GET
@ResponseMetered
@Path("/response-2xx-metered")
public Response response2xxMetered() {
return Response.ok().build();
@ResponseMetered(level = DETAILED)
@Path("/response-metered-detailed")
public Response responseMeteredDetailed(@QueryParam("status_code") @DefaultValue("200") int statusCode) {
return Response.status(Response.Status.fromStatusCode(statusCode)).build();
}

@GET
@ResponseMetered
@Path("/response-4xx-metered")
public Response response4xxMetered() {
return Response.status(Response.Status.BAD_REQUEST).build();
@ResponseMetered(level = COARSE)
@Path("/response-metered-coarse")
public Response responseMeteredCoarse(@QueryParam("status_code") @DefaultValue("200") int statusCode) {
return Response.status(Response.Status.fromStatusCode(statusCode)).build();
}

@GET
@ResponseMetered
@Path("/response-5xx-metered")
public Response response5xxMetered() {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
@ResponseMetered(level = ALL)
@Path("/response-metered-all")
public Response responseMeteredAll(@QueryParam("status_code") @DefaultValue("200") int statusCode) {
return Response.status(Response.Status.fromStatusCode(statusCode)).build();
}

@Path("/subresource")
Expand Down
Expand Up @@ -7,7 +7,9 @@
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

@ResponseMetered
import static com.codahale.metrics.annotation.ResponseMeteredLevel.ALL;

@ResponseMetered(level = ALL)
@Produces(MediaType.TEXT_PLAIN)
public class InstrumentedSubResourceResponseMeteredPerClass {
@GET
Expand Down

0 comments on commit a97b0bb

Please sign in to comment.