Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ private WebSocketMetricConstants() {
public static final String SERVER_CONNECTION_OPENED = "quarkus.websockets.server.connections.opened";

/**
* Counts number of times that opening of a WebSocket server connection resulted in error.
* Counts number of times that opening of a WebSocket server connection resulted in error, therefore closing the connection.
*/
public static final String SERVER_CONNECTION_ON_OPEN_ERROR = "quarkus.websockets.server.connections.onopen.errors";

/**
* Counts number of times that opening of a WebSocket client connection resulted in error.
* Counts number of times that opening of a WebSocket client connection resulted in error, therefore closing the connection.
*/
public static final String CLIENT_CONNECTION_OPENED_ERROR = "quarkus.websockets.client.connections.opened.errors";
public static final String CLIENT_CONNECTION_ON_OPEN_ERROR = "quarkus.websockets.client.connections.onopen.errors";

/**
* Counts all the WebSockets client closed connections.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ public WebSocketMetricsInterceptor createServerMetricsInterceptor() {
.builder(WebSocketMetricConstants.SERVER_CONNECTION_OPENED)
.description("Number of opened server connections.")
.withRegistry(meterRegistry);
final Meter.MeterProvider<Counter> connectionOpeningFailedCounter = Counter
final Meter.MeterProvider<Counter> connectionOnOpenErrorsCounter = Counter
.builder(WebSocketMetricConstants.SERVER_CONNECTION_ON_OPEN_ERROR)
.description("Number of failures occurred when opening server connection failed.")
.withRegistry(meterRegistry);
return new WebSocketMetricsInterceptorImpl(messagesCounter, bytesCounter, closedConnectionCounter, serverErrorsCounter,
connectionOpenCounter, connectionOpeningFailedCounter);
connectionOpenCounter, connectionOnOpenErrorsCounter);
}

@Override
Expand All @@ -73,12 +73,12 @@ public WebSocketMetricsInterceptor createClientMetricsInterceptor() {
.builder(WebSocketMetricConstants.CLIENT_CONNECTION_OPENED)
.description("Number of opened client connections.")
.withRegistry(meterRegistry);
final Meter.MeterProvider<Counter> connectionOpeningFailedCounter = Counter
.builder(WebSocketMetricConstants.CLIENT_CONNECTION_OPENED_ERROR)
final Meter.MeterProvider<Counter> connectionOnOpenErrorsCounter = Counter
.builder(WebSocketMetricConstants.CLIENT_CONNECTION_ON_OPEN_ERROR)
.description("Number of failures occurred when opening client connection failed.")
.withRegistry(meterRegistry);
return new WebSocketMetricsInterceptorImpl(messagesCounter, bytesCounter, closedConnectionCounter, clientErrorsCounter,
connectionOpenCounter, connectionOpeningFailedCounter);
connectionOpenCounter, connectionOnOpenErrorsCounter);
}

private static final class WebSocketMetricsInterceptorImpl implements WebSocketMetricsInterceptor {
Expand All @@ -88,18 +88,18 @@ private static final class WebSocketMetricsInterceptorImpl implements WebSocketM
private final Meter.MeterProvider<Counter> closedConnectionCounter;
private final Meter.MeterProvider<Counter> errorsCounter;
private final Meter.MeterProvider<Counter> connectionOpenCounter;
private final Meter.MeterProvider<Counter> connectionOpeningFailedCounter;
private final Meter.MeterProvider<Counter> connectionOnOpenErrorsCounter;

private WebSocketMetricsInterceptorImpl(Meter.MeterProvider<Counter> messagesCounter,
Meter.MeterProvider<Counter> bytesCounter, Meter.MeterProvider<Counter> closedConnectionCounter,
Meter.MeterProvider<Counter> errorsCounter, Meter.MeterProvider<Counter> connectionOpenCounter,
Meter.MeterProvider<Counter> connectionOpeningFailedCounter) {
Meter.MeterProvider<Counter> connectionOnOpenErrorsCounter) {
this.messagesCounter = messagesCounter;
this.bytesCounter = bytesCounter;
this.closedConnectionCounter = closedConnectionCounter;
this.errorsCounter = errorsCounter;
this.connectionOpenCounter = connectionOpenCounter;
this.connectionOpeningFailedCounter = connectionOpeningFailedCounter;
this.connectionOnOpenErrorsCounter = connectionOnOpenErrorsCounter;
}

@Override
Expand All @@ -126,7 +126,7 @@ public void onConnectionOpened(String route) {

@Override
public void onConnectionOpeningFailed(String route) {
connectionOpeningFailedCounter.withTag(URI_TAG_KEY, route).increment();
connectionOnOpenErrorsCounter.withTag(URI_TAG_KEY, route).increment();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package io.quarkus.websockets.next.test.telemetry;

import static io.quarkus.websockets.next.test.telemetry.MetricsAsserter.assertServerConnectionClosedTotal;
import static io.quarkus.websockets.next.test.telemetry.MetricsAsserter.assertServerConnectionOpenedTotal;
import static io.quarkus.websockets.next.test.telemetry.MetricsAsserter.assertServerConnectionOpeningFailedTotal;
import static io.quarkus.websockets.next.test.telemetry.MetricsAsserter.assertServerErrorTotal;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.net.URI;
import java.time.Duration;
import java.util.List;

import jakarta.inject.Inject;

import org.awaitility.Awaitility;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.builder.Version;
import io.quarkus.logging.Log;
import io.quarkus.maven.dependency.Dependency;
import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.websockets.next.OnError;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.WebSocket;
import io.quarkus.websockets.next.WebSocketClient;
import io.quarkus.websockets.next.WebSocketClientConnection;
import io.quarkus.websockets.next.WebSocketConnector;
import io.restassured.RestAssured;
import io.restassured.response.ValidatableResponse;

public class MicrometerWebSocketsStandaloneTest {

@RegisterExtension
public static final QuarkusUnitTest test = new QuarkusUnitTest()
.withApplicationRoot(root -> root
.addClasses(MetricsAsserter.class)
.addAsResource(new StringAsset("""
bounce-endpoint.prefix-responses=true
quarkus.websockets-next.server.metrics.enabled=true
quarkus.websockets-next.client.metrics.enabled=true
"""), "application.properties"))
.setForcedDependencies(
List.of(Dependency.of("io.quarkus", "quarkus-micrometer-registry-prometheus-deployment",
Version.getVersion())));

@Inject
WebSocketConnector<ErroneousClient_OnConnectError> onConnectErrorClient;

@Inject
WebSocketConnector<ErroneousClient_OnConnectErrorHandler> onConnectErrorHandlerClient;

@TestHTTPResource("/")
URI baseUri;

static ValidatableResponse getMetrics() {
return RestAssured.given().get("/q/metrics").then().statusCode(200);
}

@Test
public void testServerEndpoint_OnConnectionError() {
WebSocketClientConnection connection = onConnectErrorClient.baseUri(baseUri).connectAndAwait();
Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> {
// Connection closing is async and takes some time
assertFalse(connection.isOpen(),
"Runtime exception happened on server side and connection is still open");
});

Awaitility.await().atMost(Duration.ofSeconds(5)).untilAsserted(() -> {
getMetrics()
.body(assertServerConnectionOpeningFailedTotal("/on-connect-error", 1))
.body(assertServerErrorTotal("/on-connect-error", 1))
.body(assertServerConnectionOpenedTotal("/on-connect-error", 1))
.body(assertServerConnectionClosedTotal("/on-connect-error", 1));
});
}

@Test
public void testServerEndpoint_OnConnectionErrorHandler() {
WebSocketClientConnection connection = onConnectErrorHandlerClient.baseUri(baseUri).connectAndAwait();
Awaitility.await().atMost(Duration.ofSeconds(2)).untilAsserted(() -> {
// Connection closing will never close because there is an OnError handler.
assertTrue(connection.isOpen(), "Should never close");
});

Awaitility.await().atMost(Duration.ofSeconds(5)).untilAsserted(() -> {
getMetrics()
.body(assertServerConnectionOpeningFailedTotal("/with-on-connect-error-handler", 0))
.body(assertServerErrorTotal("/with-on-connect-error-handler", 1))
.body(assertServerConnectionOpenedTotal("/with-on-connect-error-handler", 1))
.body(assertServerConnectionClosedTotal("/with-on-connect-error-handler", 0));

});
}

// ----------------------- SERVERS -------------------------------

@WebSocket(path = "/on-connect-error")
public static class OnConnectErrorEndpoint {

@OnOpen()
public void onOpen() {
Log.info("onOpen throwing exception");
throw new RuntimeException("Crafted exception - Websocket failed to open");
}
}

@WebSocket(path = "/with-on-connect-error-handler")
public static class OnConnectErrorHandlerEndpoint {

@OnOpen()
public void onOpen() {
Log.info("onOpen throwing exception");
throw new RuntimeException("Crafted exception - Websocket failed to open");
}

@OnError
public void onError(Exception failure) {
Log.warnv("Error handled: {0}", failure.getMessage());
}
}

// ------------------------- CLIENTS -------------------------------

@WebSocketClient(path = "/on-connect-error")
public static class ErroneousClient_OnConnectError {

@OnOpen()
public void onOpen() {
Log.info("client onOpen");
}
}

@WebSocketClient(path = "/with-on-connect-error-handler")
public static class ErroneousClient_OnConnectErrorHandler {

@OnOpen()
public void onOpen() {
Log.info("client onOpen");
}
}
}
Loading