diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/metrics/MetricsRequestEventListener.java b/service/src/main/java/org/whispersystems/textsecuregcm/metrics/MetricsRequestEventListener.java index afa3cf232..752955cc9 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/metrics/MetricsRequestEventListener.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/metrics/MetricsRequestEventListener.java @@ -6,6 +6,7 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Tag; import org.glassfish.jersey.server.ExtendedUriInfo; +import org.glassfish.jersey.server.internal.process.MappableException; import org.glassfish.jersey.server.monitoring.RequestEvent; import org.glassfish.jersey.server.monitoring.RequestEventListener; @@ -22,6 +23,7 @@ class MetricsRequestEventListener implements RequestEventListener { static final String PATH_TAG = "path"; static final String STATUS_CODE_TAG = "status"; static final String TRAFFIC_SOURCE_TAG = "trafficSource"; + static final String EXCEPTION_TAG = "exception"; private final TrafficSource trafficSource; private final MeterRegistry meterRegistry; @@ -48,6 +50,14 @@ class MetricsRequestEventListener implements RequestEventListener { final List userAgentValues = event.getContainerRequest().getRequestHeader("User-Agent"); tags.addAll(UserAgentTagUtil.getUserAgentTags(userAgentValues != null ? userAgentValues.stream().findFirst().orElse(null) : null)); + if (event.getException() != null) { + if (event.getException() instanceof MappableException) { + tags.add(Tag.of(EXCEPTION_TAG, event.getException().getCause().getClass().getSimpleName())); + } else { + tags.add(Tag.of(EXCEPTION_TAG, event.getException().getClass().getSimpleName())); + } + } + meterRegistry.counter(COUNTER_NAME, tags).increment(); } } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/metrics/MetricsRequestEventListenerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/metrics/MetricsRequestEventListenerTest.java index a41742ebd..632cfc8f5 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/metrics/MetricsRequestEventListenerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/metrics/MetricsRequestEventListenerTest.java @@ -16,6 +16,7 @@ import org.glassfish.jersey.server.ContainerRequest; import org.glassfish.jersey.server.ContainerResponse; import org.glassfish.jersey.server.ExtendedUriInfo; import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.server.internal.process.MappableException; import org.glassfish.jersey.server.monitoring.RequestEvent; import org.glassfish.jersey.uri.UriTemplate; import org.junit.Before; @@ -108,6 +109,51 @@ public class MetricsRequestEventListenerTest { assertTrue(tags.contains(Tag.of(UserAgentTagUtil.VERSION_TAG, "4.53.7"))); } + @Test + @SuppressWarnings("unchecked") + public void testOnEventWithException() { + final String path = "/test"; + final int statusCode = 500; + + final ExtendedUriInfo uriInfo = mock(ExtendedUriInfo.class); + when(uriInfo.getMatchedTemplates()).thenReturn(Collections.singletonList(new UriTemplate(path))); + + final ContainerRequest request = mock(ContainerRequest.class); + when(request.getRequestHeader("User-Agent")).thenReturn(Collections.singletonList("Signal-Android 4.53.7 (Android 8.1)")); + + final ContainerResponse response = mock(ContainerResponse.class); + when(response.getStatus()).thenReturn(statusCode); + + final RequestEvent event = mock(RequestEvent.class); + when(event.getType()).thenReturn(RequestEvent.Type.FINISHED); + when(event.getUriInfo()).thenReturn(uriInfo); + when(event.getContainerRequest()).thenReturn(request); + when(event.getContainerResponse()).thenReturn(response); + when(event.getException()).thenReturn(new MappableException(new RuntimeException("OH NO"))); + + final ArgumentCaptor> tagCaptor = ArgumentCaptor.forClass(Iterable.class); + when(meterRegistry.counter(eq(MetricsRequestEventListener.COUNTER_NAME), any(Iterable.class))).thenReturn(counter); + + listener.onEvent(event); + + verify(meterRegistry).counter(eq(MetricsRequestEventListener.COUNTER_NAME), tagCaptor.capture()); + + final Iterable tagIterable = tagCaptor.getValue(); + final Set tags = new HashSet<>(); + + for (final Tag tag : tagIterable) { + tags.add(tag); + } + + assertEquals(6, tags.size()); + assertTrue(tags.contains(Tag.of(MetricsRequestEventListener.PATH_TAG, path))); + assertTrue(tags.contains(Tag.of(MetricsRequestEventListener.STATUS_CODE_TAG, String.valueOf(statusCode)))); + assertTrue(tags.contains(Tag.of(MetricsRequestEventListener.TRAFFIC_SOURCE_TAG, TRAFFIC_SOURCE.name().toLowerCase()))); + assertTrue(tags.contains(Tag.of(UserAgentTagUtil.PLATFORM_TAG, "android"))); + assertTrue(tags.contains(Tag.of(UserAgentTagUtil.VERSION_TAG, "4.53.7"))); + assertTrue(tags.contains(Tag.of(MetricsRequestEventListener.EXCEPTION_TAG, "RuntimeException"))); + } + @Test public void testGetPathTemplate() { final UriTemplate firstComponent = new UriTemplate("/first");