Instrument request/response sizes
This commit is contained in:
parent
295cedc075
commit
5de848bf38
|
@ -59,6 +59,10 @@ public class MetricsHttpChannelListener implements HttpChannel.Listener, Contain
|
||||||
public static final String REQUEST_COUNTER_NAME = MetricsRequestEventListener.REQUEST_COUNTER_NAME;
|
public static final String REQUEST_COUNTER_NAME = MetricsRequestEventListener.REQUEST_COUNTER_NAME;
|
||||||
public static final String REQUESTS_BY_VERSION_COUNTER_NAME = MetricsRequestEventListener.REQUESTS_BY_VERSION_COUNTER_NAME;
|
public static final String REQUESTS_BY_VERSION_COUNTER_NAME = MetricsRequestEventListener.REQUESTS_BY_VERSION_COUNTER_NAME;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
static final String RESPONSE_BYTES_COUNTER_NAME = MetricsRequestEventListener.RESPONSE_BYTES_COUNTER_NAME;
|
||||||
|
@VisibleForTesting
|
||||||
|
static final String REQUEST_BYTES_COUNTER_NAME = MetricsRequestEventListener.REQUEST_BYTES_COUNTER_NAME;
|
||||||
|
@VisibleForTesting
|
||||||
static final String URI_INFO_PROPERTY_NAME = MetricsHttpChannelListener.class.getName() + ".uriInfo";
|
static final String URI_INFO_PROPERTY_NAME = MetricsHttpChannelListener.class.getName() + ".uriInfo";
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
@ -141,6 +145,9 @@ public class MetricsHttpChannelListener implements HttpChannel.Listener, Contain
|
||||||
|
|
||||||
meterRegistry.counter(REQUEST_COUNTER_NAME, tags).increment();
|
meterRegistry.counter(REQUEST_COUNTER_NAME, tags).increment();
|
||||||
|
|
||||||
|
meterRegistry.counter(RESPONSE_BYTES_COUNTER_NAME, tags).increment(request.getResponse().getContentCount());
|
||||||
|
meterRegistry.counter(REQUEST_BYTES_COUNTER_NAME, tags).increment(request.getContentRead());
|
||||||
|
|
||||||
UserAgentTagUtil.getClientVersionTag(requestInfo.userAgent(), clientReleaseManager).ifPresent(
|
UserAgentTagUtil.getClientVersionTag(requestInfo.userAgent(), clientReleaseManager).ifPresent(
|
||||||
clientVersionTag -> meterRegistry.counter(REQUESTS_BY_VERSION_COUNTER_NAME,
|
clientVersionTag -> meterRegistry.counter(REQUESTS_BY_VERSION_COUNTER_NAME,
|
||||||
Tags.of(clientVersionTag, UserAgentTagUtil.getPlatformTag(requestInfo.userAgent()))).increment());
|
Tags.of(clientVersionTag, UserAgentTagUtil.getPlatformTag(requestInfo.userAgent()))).increment());
|
||||||
|
|
|
@ -15,8 +15,11 @@ import io.micrometer.core.instrument.Tags;
|
||||||
import org.glassfish.jersey.server.ContainerResponse;
|
import org.glassfish.jersey.server.ContainerResponse;
|
||||||
import org.glassfish.jersey.server.monitoring.RequestEvent;
|
import org.glassfish.jersey.server.monitoring.RequestEvent;
|
||||||
import org.glassfish.jersey.server.monitoring.RequestEventListener;
|
import org.glassfish.jersey.server.monitoring.RequestEventListener;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.storage.ClientReleaseManager;
|
import org.whispersystems.textsecuregcm.storage.ClientReleaseManager;
|
||||||
import org.whispersystems.textsecuregcm.util.logging.UriInfoUtil;
|
import org.whispersystems.textsecuregcm.util.logging.UriInfoUtil;
|
||||||
|
import org.whispersystems.websocket.WebSocketResourceProvider;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -24,14 +27,19 @@ import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gathers and reports request-level metrics.
|
* Gathers and reports request-level metrics for WebSocket traffic only.
|
||||||
|
* For HTTP traffic, use {@link MetricsHttpChannelListener}.
|
||||||
*/
|
*/
|
||||||
public class MetricsRequestEventListener implements RequestEventListener {
|
public class MetricsRequestEventListener implements RequestEventListener {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(MetricsRequestEventListener.class);
|
||||||
|
|
||||||
private final ClientReleaseManager clientReleaseManager;
|
private final ClientReleaseManager clientReleaseManager;
|
||||||
|
|
||||||
public static final String REQUEST_COUNTER_NAME = MetricRegistry.name(MetricsRequestEventListener.class, "request");
|
public static final String REQUEST_COUNTER_NAME = MetricRegistry.name(MetricsRequestEventListener.class, "request");
|
||||||
public static final String REQUESTS_BY_VERSION_COUNTER_NAME = MetricRegistry.name(MetricsRequestEventListener.class, "requestByVersion");
|
public static final String REQUESTS_BY_VERSION_COUNTER_NAME = MetricRegistry.name(MetricsRequestEventListener.class, "requestByVersion");
|
||||||
|
public static final String RESPONSE_BYTES_COUNTER_NAME = MetricRegistry.name(MetricsRequestEventListener.class, "responseBytes");
|
||||||
|
public static final String REQUEST_BYTES_COUNTER_NAME = MetricRegistry.name(MetricsRequestEventListener.class, "requestBytes");
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final String PATH_TAG = "path";
|
static final String PATH_TAG = "path";
|
||||||
|
@ -50,6 +58,10 @@ public class MetricsRequestEventListener implements RequestEventListener {
|
||||||
|
|
||||||
public MetricsRequestEventListener(final TrafficSource trafficSource, final ClientReleaseManager clientReleaseManager) {
|
public MetricsRequestEventListener(final TrafficSource trafficSource, final ClientReleaseManager clientReleaseManager) {
|
||||||
this(trafficSource, Metrics.globalRegistry, clientReleaseManager);
|
this(trafficSource, Metrics.globalRegistry, clientReleaseManager);
|
||||||
|
|
||||||
|
if (trafficSource == TrafficSource.HTTP) {
|
||||||
|
logger.warn("Use {} for HTTP traffic", MetricsHttpChannelListener.class.getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
@ -85,6 +97,18 @@ public class MetricsRequestEventListener implements RequestEventListener {
|
||||||
|
|
||||||
meterRegistry.counter(REQUEST_COUNTER_NAME, tags).increment();
|
meterRegistry.counter(REQUEST_COUNTER_NAME, tags).increment();
|
||||||
|
|
||||||
|
Optional.ofNullable(event.getContainerRequest().getProperty(WebSocketResourceProvider.REQUEST_LENGTH_PROPERTY))
|
||||||
|
.filter(Integer.class::isInstance)
|
||||||
|
.map(Integer.class::cast)
|
||||||
|
.filter(bytes -> bytes >= 0)
|
||||||
|
.ifPresent(bytes -> meterRegistry.counter(REQUEST_BYTES_COUNTER_NAME, tags).increment(bytes));
|
||||||
|
|
||||||
|
Optional.ofNullable(event.getContainerRequest().getProperty(WebSocketResourceProvider.RESPONSE_LENGTH_PROPERTY))
|
||||||
|
.filter(Integer.class::isInstance)
|
||||||
|
.map(Integer.class::cast)
|
||||||
|
.filter(bytes -> bytes >= 0)
|
||||||
|
.ifPresent(bytes -> meterRegistry.counter(RESPONSE_BYTES_COUNTER_NAME, tags).increment(bytes));
|
||||||
|
|
||||||
UserAgentTagUtil.getClientVersionTag(userAgent, clientReleaseManager)
|
UserAgentTagUtil.getClientVersionTag(userAgent, clientReleaseManager)
|
||||||
.ifPresent(clientVersionTag -> meterRegistry.counter(REQUESTS_BY_VERSION_COUNTER_NAME,
|
.ifPresent(clientVersionTag -> meterRegistry.counter(REQUESTS_BY_VERSION_COUNTER_NAME,
|
||||||
Tags.of(clientVersionTag, UserAgentTagUtil.getPlatformTag(userAgent)))
|
Tags.of(clientVersionTag, UserAgentTagUtil.getPlatformTag(userAgent)))
|
||||||
|
|
|
@ -46,6 +46,7 @@ import java.security.Principal;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -83,7 +84,9 @@ class MetricsHttpChannelListenerIntegrationTest {
|
||||||
|
|
||||||
private static final TrafficSource TRAFFIC_SOURCE = TrafficSource.HTTP;
|
private static final TrafficSource TRAFFIC_SOURCE = TrafficSource.HTTP;
|
||||||
private static final MeterRegistry METER_REGISTRY = mock(MeterRegistry.class);
|
private static final MeterRegistry METER_REGISTRY = mock(MeterRegistry.class);
|
||||||
private static final Counter COUNTER = mock(Counter.class);
|
private static final Counter REQUEST_COUNTER = mock(Counter.class);
|
||||||
|
private static final Counter RESPONSE_BYTES_COUNTER = mock(Counter.class);
|
||||||
|
private static final Counter REQUEST_BYTES_COUNTER = mock(Counter.class);
|
||||||
private static final AtomicReference<CountDownLatch> COUNT_DOWN_LATCH_FUTURE_REFERENCE = new AtomicReference<>();
|
private static final AtomicReference<CountDownLatch> COUNT_DOWN_LATCH_FUTURE_REFERENCE = new AtomicReference<>();
|
||||||
|
|
||||||
private static final DropwizardAppExtension<Configuration> EXTENSION = new DropwizardAppExtension<>(
|
private static final DropwizardAppExtension<Configuration> EXTENSION = new DropwizardAppExtension<>(
|
||||||
|
@ -92,7 +95,9 @@ class MetricsHttpChannelListenerIntegrationTest {
|
||||||
@AfterEach
|
@AfterEach
|
||||||
void teardown() {
|
void teardown() {
|
||||||
reset(METER_REGISTRY);
|
reset(METER_REGISTRY);
|
||||||
reset(COUNTER);
|
reset(REQUEST_COUNTER);
|
||||||
|
reset(RESPONSE_BYTES_COUNTER);
|
||||||
|
reset(REQUEST_BYTES_COUNTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -105,11 +110,13 @@ class MetricsHttpChannelListenerIntegrationTest {
|
||||||
COUNT_DOWN_LATCH_FUTURE_REFERENCE.set(countDownLatch);
|
COUNT_DOWN_LATCH_FUTURE_REFERENCE.set(countDownLatch);
|
||||||
|
|
||||||
final ArgumentCaptor<Iterable<Tag>> tagCaptor = ArgumentCaptor.forClass(Iterable.class);
|
final ArgumentCaptor<Iterable<Tag>> tagCaptor = ArgumentCaptor.forClass(Iterable.class);
|
||||||
|
final Map<String, Counter> counterMap = Map.of(
|
||||||
|
MetricsHttpChannelListener.REQUEST_COUNTER_NAME, REQUEST_COUNTER,
|
||||||
|
MetricsHttpChannelListener.RESPONSE_BYTES_COUNTER_NAME, RESPONSE_BYTES_COUNTER,
|
||||||
|
MetricsHttpChannelListener.REQUEST_BYTES_COUNTER_NAME, REQUEST_BYTES_COUNTER
|
||||||
|
);
|
||||||
when(METER_REGISTRY.counter(anyString(), any(Iterable.class)))
|
when(METER_REGISTRY.counter(anyString(), any(Iterable.class)))
|
||||||
.thenAnswer(a -> MetricsHttpChannelListener.REQUEST_COUNTER_NAME.equals(a.getArgument(0, String.class))
|
.thenAnswer(a -> counterMap.getOrDefault(a.getArgument(0, String.class), mock(Counter.class)));
|
||||||
? COUNTER
|
|
||||||
: mock(Counter.class))
|
|
||||||
.thenReturn(COUNTER);
|
|
||||||
|
|
||||||
Client client = EXTENSION.client();
|
Client client = EXTENSION.client();
|
||||||
|
|
||||||
|
@ -141,7 +148,7 @@ class MetricsHttpChannelListenerIntegrationTest {
|
||||||
assertTrue(countDownLatch.await(1000, TimeUnit.MILLISECONDS));
|
assertTrue(countDownLatch.await(1000, TimeUnit.MILLISECONDS));
|
||||||
|
|
||||||
verify(METER_REGISTRY).counter(eq(MetricsHttpChannelListener.REQUEST_COUNTER_NAME), tagCaptor.capture());
|
verify(METER_REGISTRY).counter(eq(MetricsHttpChannelListener.REQUEST_COUNTER_NAME), tagCaptor.capture());
|
||||||
verify(COUNTER).increment();
|
verify(REQUEST_COUNTER).increment();
|
||||||
|
|
||||||
final Iterable<Tag> tagIterable = tagCaptor.getValue();
|
final Iterable<Tag> tagIterable = tagCaptor.getValue();
|
||||||
final Set<Tag> tags = new HashSet<>();
|
final Set<Tag> tags = new HashSet<>();
|
||||||
|
@ -186,11 +193,13 @@ class MetricsHttpChannelListenerIntegrationTest {
|
||||||
COUNT_DOWN_LATCH_FUTURE_REFERENCE.set(countDownLatch);
|
COUNT_DOWN_LATCH_FUTURE_REFERENCE.set(countDownLatch);
|
||||||
|
|
||||||
final ArgumentCaptor<Iterable<Tag>> tagCaptor = ArgumentCaptor.forClass(Iterable.class);
|
final ArgumentCaptor<Iterable<Tag>> tagCaptor = ArgumentCaptor.forClass(Iterable.class);
|
||||||
|
final Map<String, Counter> counterMap = Map.of(
|
||||||
|
MetricsHttpChannelListener.REQUEST_COUNTER_NAME, REQUEST_COUNTER,
|
||||||
|
MetricsHttpChannelListener.RESPONSE_BYTES_COUNTER_NAME, RESPONSE_BYTES_COUNTER,
|
||||||
|
MetricsHttpChannelListener.REQUEST_BYTES_COUNTER_NAME, REQUEST_BYTES_COUNTER
|
||||||
|
);
|
||||||
when(METER_REGISTRY.counter(anyString(), any(Iterable.class)))
|
when(METER_REGISTRY.counter(anyString(), any(Iterable.class)))
|
||||||
.thenAnswer(a -> MetricsHttpChannelListener.REQUEST_COUNTER_NAME.equals(a.getArgument(0, String.class))
|
.thenAnswer(a -> counterMap.getOrDefault(a.getArgument(0, String.class), mock(Counter.class)));
|
||||||
? COUNTER
|
|
||||||
: mock(Counter.class))
|
|
||||||
.thenReturn(COUNTER);
|
|
||||||
|
|
||||||
client.connect(new WebSocketListener() {
|
client.connect(new WebSocketListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -203,7 +212,7 @@ class MetricsHttpChannelListenerIntegrationTest {
|
||||||
assertTrue(countDownLatch.await(1000, TimeUnit.MILLISECONDS));
|
assertTrue(countDownLatch.await(1000, TimeUnit.MILLISECONDS));
|
||||||
|
|
||||||
verify(METER_REGISTRY).counter(eq(MetricsHttpChannelListener.REQUEST_COUNTER_NAME), tagCaptor.capture());
|
verify(METER_REGISTRY).counter(eq(MetricsHttpChannelListener.REQUEST_COUNTER_NAME), tagCaptor.capture());
|
||||||
verify(COUNTER).increment();
|
verify(REQUEST_COUNTER).increment();
|
||||||
|
|
||||||
final Iterable<Tag> tagIterable = tagCaptor.getValue();
|
final Iterable<Tag> tagIterable = tagCaptor.getValue();
|
||||||
final Set<Tag> tags = new HashSet<>();
|
final Set<Tag> tags = new HashSet<>();
|
||||||
|
|
|
@ -39,6 +39,8 @@ class MetricsHttpChannelListenerTest {
|
||||||
private MeterRegistry meterRegistry;
|
private MeterRegistry meterRegistry;
|
||||||
private Counter requestCounter;
|
private Counter requestCounter;
|
||||||
private Counter requestsByVersionCounter;
|
private Counter requestsByVersionCounter;
|
||||||
|
private Counter responseBytesCounter;
|
||||||
|
private Counter requestBytesCounter;
|
||||||
private ClientReleaseManager clientReleaseManager;
|
private ClientReleaseManager clientReleaseManager;
|
||||||
private MetricsHttpChannelListener listener;
|
private MetricsHttpChannelListener listener;
|
||||||
|
|
||||||
|
@ -47,6 +49,8 @@ class MetricsHttpChannelListenerTest {
|
||||||
meterRegistry = mock(MeterRegistry.class);
|
meterRegistry = mock(MeterRegistry.class);
|
||||||
requestCounter = mock(Counter.class);
|
requestCounter = mock(Counter.class);
|
||||||
requestsByVersionCounter = mock(Counter.class);
|
requestsByVersionCounter = mock(Counter.class);
|
||||||
|
responseBytesCounter = mock(Counter.class);
|
||||||
|
requestBytesCounter = mock(Counter.class);
|
||||||
|
|
||||||
when(meterRegistry.counter(eq(MetricsHttpChannelListener.REQUEST_COUNTER_NAME), any(Iterable.class)))
|
when(meterRegistry.counter(eq(MetricsHttpChannelListener.REQUEST_COUNTER_NAME), any(Iterable.class)))
|
||||||
.thenReturn(requestCounter);
|
.thenReturn(requestCounter);
|
||||||
|
@ -54,6 +58,12 @@ class MetricsHttpChannelListenerTest {
|
||||||
when(meterRegistry.counter(eq(MetricsHttpChannelListener.REQUESTS_BY_VERSION_COUNTER_NAME), any(Iterable.class)))
|
when(meterRegistry.counter(eq(MetricsHttpChannelListener.REQUESTS_BY_VERSION_COUNTER_NAME), any(Iterable.class)))
|
||||||
.thenReturn(requestsByVersionCounter);
|
.thenReturn(requestsByVersionCounter);
|
||||||
|
|
||||||
|
when(meterRegistry.counter(eq(MetricsHttpChannelListener.RESPONSE_BYTES_COUNTER_NAME), any(Iterable.class)))
|
||||||
|
.thenReturn(responseBytesCounter);
|
||||||
|
|
||||||
|
when(meterRegistry.counter(eq(MetricsHttpChannelListener.REQUEST_BYTES_COUNTER_NAME), any(Iterable.class)))
|
||||||
|
.thenReturn(requestBytesCounter);
|
||||||
|
|
||||||
clientReleaseManager = mock(ClientReleaseManager.class);
|
clientReleaseManager = mock(ClientReleaseManager.class);
|
||||||
|
|
||||||
listener = new MetricsHttpChannelListener(meterRegistry, clientReleaseManager, Collections.emptySet());
|
listener = new MetricsHttpChannelListener(meterRegistry, clientReleaseManager, Collections.emptySet());
|
||||||
|
@ -76,7 +86,9 @@ class MetricsHttpChannelListenerTest {
|
||||||
|
|
||||||
final Response response = mock(Response.class);
|
final Response response = mock(Response.class);
|
||||||
when(response.getStatus()).thenReturn(statusCode);
|
when(response.getStatus()).thenReturn(statusCode);
|
||||||
|
when(response.getContentCount()).thenReturn(1024L);
|
||||||
when(request.getResponse()).thenReturn(response);
|
when(request.getResponse()).thenReturn(response);
|
||||||
|
when(request.getContentRead()).thenReturn(512L);
|
||||||
final ExtendedUriInfo extendedUriInfo = mock(ExtendedUriInfo.class);
|
final ExtendedUriInfo extendedUriInfo = mock(ExtendedUriInfo.class);
|
||||||
when(request.getAttribute(MetricsHttpChannelListener.URI_INFO_PROPERTY_NAME)).thenReturn(extendedUriInfo);
|
when(request.getAttribute(MetricsHttpChannelListener.URI_INFO_PROPERTY_NAME)).thenReturn(extendedUriInfo);
|
||||||
when(extendedUriInfo.getMatchedTemplates()).thenReturn(List.of(new UriTemplate(path)));
|
when(extendedUriInfo.getMatchedTemplates()).thenReturn(List.of(new UriTemplate(path)));
|
||||||
|
@ -87,6 +99,9 @@ class MetricsHttpChannelListenerTest {
|
||||||
|
|
||||||
verify(requestCounter).increment();
|
verify(requestCounter).increment();
|
||||||
|
|
||||||
|
verify(responseBytesCounter).increment(1024L);
|
||||||
|
verify(requestBytesCounter).increment(512L);
|
||||||
|
|
||||||
verify(meterRegistry).counter(eq(MetricsHttpChannelListener.REQUEST_COUNTER_NAME), tagCaptor.capture());
|
verify(meterRegistry).counter(eq(MetricsHttpChannelListener.REQUEST_COUNTER_NAME), tagCaptor.capture());
|
||||||
|
|
||||||
final Set<Tag> tags = new HashSet<>();
|
final Set<Tag> tags = new HashSet<>();
|
||||||
|
@ -123,7 +138,9 @@ class MetricsHttpChannelListenerTest {
|
||||||
|
|
||||||
final Response response = mock(Response.class);
|
final Response response = mock(Response.class);
|
||||||
when(response.getStatus()).thenReturn(statusCode);
|
when(response.getStatus()).thenReturn(statusCode);
|
||||||
|
when(response.getContentCount()).thenReturn(1024L);
|
||||||
when(request.getResponse()).thenReturn(response);
|
when(request.getResponse()).thenReturn(response);
|
||||||
|
when(request.getContentRead()).thenReturn(512L);
|
||||||
final ExtendedUriInfo extendedUriInfo = mock(ExtendedUriInfo.class);
|
final ExtendedUriInfo extendedUriInfo = mock(ExtendedUriInfo.class);
|
||||||
when(request.getAttribute(MetricsHttpChannelListener.URI_INFO_PROPERTY_NAME)).thenReturn(extendedUriInfo);
|
when(request.getAttribute(MetricsHttpChannelListener.URI_INFO_PROPERTY_NAME)).thenReturn(extendedUriInfo);
|
||||||
when(extendedUriInfo.getMatchedTemplates()).thenReturn(List.of(new UriTemplate(path)));
|
when(extendedUriInfo.getMatchedTemplates()).thenReturn(List.of(new UriTemplate(path)));
|
||||||
|
|
|
@ -62,6 +62,8 @@ class MetricsRequestEventListenerTest {
|
||||||
|
|
||||||
private MeterRegistry meterRegistry;
|
private MeterRegistry meterRegistry;
|
||||||
private Counter counter;
|
private Counter counter;
|
||||||
|
private Counter responseBytesCounter;
|
||||||
|
private Counter requestBytesCounter;
|
||||||
private MetricsRequestEventListener listener;
|
private MetricsRequestEventListener listener;
|
||||||
|
|
||||||
private static final TrafficSource TRAFFIC_SOURCE = TrafficSource.HTTP;
|
private static final TrafficSource TRAFFIC_SOURCE = TrafficSource.HTTP;
|
||||||
|
@ -70,6 +72,8 @@ class MetricsRequestEventListenerTest {
|
||||||
void setup() {
|
void setup() {
|
||||||
meterRegistry = mock(MeterRegistry.class);
|
meterRegistry = mock(MeterRegistry.class);
|
||||||
counter = mock(Counter.class);
|
counter = mock(Counter.class);
|
||||||
|
responseBytesCounter = mock(Counter.class);
|
||||||
|
requestBytesCounter = mock(Counter.class);
|
||||||
|
|
||||||
final ClientReleaseManager clientReleaseManager = mock(ClientReleaseManager.class);
|
final ClientReleaseManager clientReleaseManager = mock(ClientReleaseManager.class);
|
||||||
when(clientReleaseManager.isVersionActive(any(), any())).thenReturn(false);
|
when(clientReleaseManager.isVersionActive(any(), any())).thenReturn(false);
|
||||||
|
@ -91,6 +95,8 @@ class MetricsRequestEventListenerTest {
|
||||||
when(request.getMethod()).thenReturn(method);
|
when(request.getMethod()).thenReturn(method);
|
||||||
when(request.getRequestHeader(HttpHeaders.USER_AGENT)).thenReturn(
|
when(request.getRequestHeader(HttpHeaders.USER_AGENT)).thenReturn(
|
||||||
Collections.singletonList("Signal-Android/7.6.2 Android/34 libsignal/0.46.0"));
|
Collections.singletonList("Signal-Android/7.6.2 Android/34 libsignal/0.46.0"));
|
||||||
|
when(request.getProperty(WebSocketResourceProvider.REQUEST_LENGTH_PROPERTY)).thenReturn(512);
|
||||||
|
when(request.getProperty(WebSocketResourceProvider.RESPONSE_LENGTH_PROPERTY)).thenReturn(1024);
|
||||||
|
|
||||||
final ContainerResponse response = mock(ContainerResponse.class);
|
final ContainerResponse response = mock(ContainerResponse.class);
|
||||||
when(response.getStatus()).thenReturn(statusCode);
|
when(response.getStatus()).thenReturn(statusCode);
|
||||||
|
@ -104,10 +110,17 @@ class MetricsRequestEventListenerTest {
|
||||||
final ArgumentCaptor<Iterable<Tag>> tagCaptor = ArgumentCaptor.forClass(Iterable.class);
|
final ArgumentCaptor<Iterable<Tag>> tagCaptor = ArgumentCaptor.forClass(Iterable.class);
|
||||||
when(meterRegistry.counter(eq(MetricsRequestEventListener.REQUEST_COUNTER_NAME), any(Iterable.class)))
|
when(meterRegistry.counter(eq(MetricsRequestEventListener.REQUEST_COUNTER_NAME), any(Iterable.class)))
|
||||||
.thenReturn(counter);
|
.thenReturn(counter);
|
||||||
|
when(meterRegistry.counter(eq(MetricsRequestEventListener.RESPONSE_BYTES_COUNTER_NAME), any(Iterable.class)))
|
||||||
|
.thenReturn(responseBytesCounter);
|
||||||
|
when(meterRegistry.counter(eq(MetricsRequestEventListener.REQUEST_BYTES_COUNTER_NAME), any(Iterable.class)))
|
||||||
|
.thenReturn(requestBytesCounter);
|
||||||
|
|
||||||
listener.onEvent(event);
|
listener.onEvent(event);
|
||||||
|
|
||||||
verify(meterRegistry).counter(eq(MetricsRequestEventListener.REQUEST_COUNTER_NAME), tagCaptor.capture());
|
verify(meterRegistry).counter(eq(MetricsRequestEventListener.REQUEST_COUNTER_NAME), tagCaptor.capture());
|
||||||
|
verify(counter).increment();
|
||||||
|
verify(responseBytesCounter).increment(1024L);
|
||||||
|
verify(requestBytesCounter).increment(512L);
|
||||||
|
|
||||||
final Iterable<Tag> tagIterable = tagCaptor.getValue();
|
final Iterable<Tag> tagIterable = tagCaptor.getValue();
|
||||||
final Set<Tag> tags = new HashSet<>();
|
final Set<Tag> tags = new HashSet<>();
|
||||||
|
@ -155,6 +168,10 @@ class MetricsRequestEventListenerTest {
|
||||||
final ArgumentCaptor<Iterable<Tag>> tagCaptor = ArgumentCaptor.forClass(Iterable.class);
|
final ArgumentCaptor<Iterable<Tag>> tagCaptor = ArgumentCaptor.forClass(Iterable.class);
|
||||||
when(meterRegistry.counter(eq(MetricsRequestEventListener.REQUEST_COUNTER_NAME), any(Iterable.class)))
|
when(meterRegistry.counter(eq(MetricsRequestEventListener.REQUEST_COUNTER_NAME), any(Iterable.class)))
|
||||||
.thenReturn(counter);
|
.thenReturn(counter);
|
||||||
|
when(meterRegistry.counter(eq(MetricsRequestEventListener.RESPONSE_BYTES_COUNTER_NAME), any(Iterable.class)))
|
||||||
|
.thenReturn(responseBytesCounter);
|
||||||
|
when(meterRegistry.counter(eq(MetricsRequestEventListener.REQUEST_BYTES_COUNTER_NAME), any(Iterable.class)))
|
||||||
|
.thenReturn(requestBytesCounter);
|
||||||
|
|
||||||
provider.onWebSocketConnect(session);
|
provider.onWebSocketConnect(session);
|
||||||
|
|
||||||
|
@ -216,6 +233,10 @@ class MetricsRequestEventListenerTest {
|
||||||
final ArgumentCaptor<Iterable<Tag>> tagCaptor = ArgumentCaptor.forClass(Iterable.class);
|
final ArgumentCaptor<Iterable<Tag>> tagCaptor = ArgumentCaptor.forClass(Iterable.class);
|
||||||
when(meterRegistry.counter(eq(MetricsRequestEventListener.REQUEST_COUNTER_NAME), any(Iterable.class))).thenReturn(
|
when(meterRegistry.counter(eq(MetricsRequestEventListener.REQUEST_COUNTER_NAME), any(Iterable.class))).thenReturn(
|
||||||
counter);
|
counter);
|
||||||
|
when(meterRegistry.counter(eq(MetricsRequestEventListener.RESPONSE_BYTES_COUNTER_NAME), any(Iterable.class)))
|
||||||
|
.thenReturn(responseBytesCounter);
|
||||||
|
when(meterRegistry.counter(eq(MetricsRequestEventListener.REQUEST_BYTES_COUNTER_NAME), any(Iterable.class)))
|
||||||
|
.thenReturn(requestBytesCounter);
|
||||||
|
|
||||||
provider.onWebSocketConnect(session);
|
provider.onWebSocketConnect(session);
|
||||||
|
|
||||||
|
|
|
@ -173,18 +173,32 @@ public class WebSocketResourceProvider<T extends Principal> implements WebSocket
|
||||||
* {@link org.whispersystems.websocket.ReusableAuth.MutableRef} for us to close when the request is finished
|
* {@link org.whispersystems.websocket.ReusableAuth.MutableRef} for us to close when the request is finished
|
||||||
*/
|
*/
|
||||||
public static final String RESOLVED_PRINCIPAL_PROPERTY = WebSocketResourceProvider.class.getName() + ".resolvedPrincipal";
|
public static final String RESOLVED_PRINCIPAL_PROPERTY = WebSocketResourceProvider.class.getName() + ".resolvedPrincipal";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The property name where request byte count is stored for metrics collection
|
||||||
|
*/
|
||||||
|
public static final String REQUEST_LENGTH_PROPERTY = WebSocketResourceProvider.class.getName() + ".requestBytes";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The property name where response byte count is stored for metrics collection
|
||||||
|
*/
|
||||||
|
public static final String RESPONSE_LENGTH_PROPERTY = WebSocketResourceProvider.class.getName() + ".responseBytes";
|
||||||
|
|
||||||
private void handleRequest(WebSocketRequestMessage requestMessage) {
|
private void handleRequest(WebSocketRequestMessage requestMessage) {
|
||||||
ContainerRequest containerRequest = new ContainerRequest(null, URI.create(requestMessage.getPath()),
|
ContainerRequest containerRequest = new ContainerRequest(null, URI.create(requestMessage.getPath()),
|
||||||
requestMessage.getVerb(), new WebSocketSecurityContext(new ContextPrincipal(context)),
|
requestMessage.getVerb(), new WebSocketSecurityContext(new ContextPrincipal(context)),
|
||||||
new MapPropertiesDelegate(new HashMap<>()), jerseyHandler.getConfiguration());
|
new MapPropertiesDelegate(new HashMap<>()), jerseyHandler.getConfiguration());
|
||||||
containerRequest.headers(getCombinedHeaders(session.getUpgradeRequest().getHeaders(), requestMessage.getHeaders()));
|
containerRequest.headers(getCombinedHeaders(session.getUpgradeRequest().getHeaders(), requestMessage.getHeaders()));
|
||||||
|
|
||||||
|
final int requestBytes = requestMessage.getBody().map(body -> body.length).orElse(0);
|
||||||
|
|
||||||
if (requestMessage.getBody().isPresent()) {
|
if (requestMessage.getBody().isPresent()) {
|
||||||
containerRequest.setEntityStream(new ByteArrayInputStream(requestMessage.getBody().get()));
|
containerRequest.setEntityStream(new ByteArrayInputStream(requestMessage.getBody().get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
containerRequest.setProperty(remoteAddressPropertyName, remoteAddress);
|
containerRequest.setProperty(remoteAddressPropertyName, remoteAddress);
|
||||||
containerRequest.setProperty(REUSABLE_AUTH_PROPERTY, reusableAuth);
|
containerRequest.setProperty(REUSABLE_AUTH_PROPERTY, reusableAuth);
|
||||||
|
containerRequest.setProperty(REQUEST_LENGTH_PROPERTY, requestBytes);
|
||||||
|
|
||||||
ByteArrayOutputStream responseBody = new ByteArrayOutputStream();
|
ByteArrayOutputStream responseBody = new ByteArrayOutputStream();
|
||||||
CompletableFuture<ContainerResponse> responseFuture = (CompletableFuture<ContainerResponse>) jerseyHandler.apply(
|
CompletableFuture<ContainerResponse> responseFuture = (CompletableFuture<ContainerResponse>) jerseyHandler.apply(
|
||||||
|
@ -203,6 +217,8 @@ public class WebSocketResourceProvider<T extends Principal> implements WebSocket
|
||||||
})
|
})
|
||||||
.thenAccept(response -> {
|
.thenAccept(response -> {
|
||||||
try {
|
try {
|
||||||
|
final int responseBytes = responseBody.size();
|
||||||
|
containerRequest.setProperty(RESPONSE_LENGTH_PROPERTY, responseBytes);
|
||||||
sendResponse(requestMessage, response, responseBody);
|
sendResponse(requestMessage, response, responseBody);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
@ -213,6 +229,7 @@ public class WebSocketResourceProvider<T extends Principal> implements WebSocket
|
||||||
logger.warn("Websocket Error: " + requestMessage.getVerb() + " " + requestMessage.getPath() + "\n"
|
logger.warn("Websocket Error: " + requestMessage.getVerb() + " " + requestMessage.getPath() + "\n"
|
||||||
+ requestMessage.getBody(), exception);
|
+ requestMessage.getBody(), exception);
|
||||||
try {
|
try {
|
||||||
|
containerRequest.setProperty(RESPONSE_LENGTH_PROPERTY, 0);
|
||||||
sendErrorResponse(requestMessage, Response.status(500).build());
|
sendErrorResponse(requestMessage, Response.status(500).build());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.warn("Failed to send error response", e);
|
logger.warn("Failed to send error response", e);
|
||||||
|
|
Loading…
Reference in New Issue