Measure the rate of new WebSocket connections by authentication type

This commit is contained in:
Jon Chambers
2025-07-17 11:35:15 -04:00
committed by GitHub
parent 4ccd39fd55
commit 631b9a5290
3 changed files with 33 additions and 26 deletions

View File

@@ -18,26 +18,35 @@ public class OpenWebSocketCounter {
private static final String WEBSOCKET_CLOSED_COUNTER_NAME = name(OpenWebSocketCounter.class, "websocketClosed");
private final String newConnectionCounterName;
private final String durationTimerName;
private final Tags tags;
private final Map<ClientPlatform, AtomicInteger> openWebsocketsByClientPlatform;
private final AtomicInteger openWebsocketsFromUnknownPlatforms;
private final Map<ClientPlatform, Timer> durationTimersByClientPlatform;
private final Timer durationTimerForUnknownPlatforms;
public OpenWebSocketCounter(final String openWebSocketGaugeName,
final String newConnectionCounterName,
final String durationTimerName) {
public OpenWebSocketCounter(final String openWebSocketGaugeName, final String durationTimerName) {
this(openWebSocketGaugeName, durationTimerName, Tags.empty());
this(openWebSocketGaugeName, durationTimerName, newConnectionCounterName, Tags.empty());
}
public OpenWebSocketCounter(final String openWebSocketGaugeName, final String durationTimerName, final Tags tags) {
public OpenWebSocketCounter(final String openWebSocketGaugeName,
final String newConnectionCounterName,
final String durationTimerName,
final Tags tags) {
this.newConnectionCounterName = newConnectionCounterName;
this.durationTimerName = durationTimerName;
this.tags = tags;
openWebsocketsByClientPlatform = EnumMapUtil.toEnumMap(ClientPlatform.class,
clientPlatform -> buildGauge(openWebSocketGaugeName, clientPlatform.name().toLowerCase(), tags));
openWebsocketsFromUnknownPlatforms = buildGauge(openWebSocketGaugeName, "unknown", tags);
durationTimersByClientPlatform = EnumMapUtil.toEnumMap(ClientPlatform.class,
clientPlatform -> buildTimer(durationTimerName, clientPlatform.name().toLowerCase(), tags));
durationTimerForUnknownPlatforms = buildTimer(durationTimerName, "unknown", tags);
}
private static AtomicInteger buildGauge(final String gaugeName, final String clientPlatformName, final Tags tags) {
@@ -46,47 +55,43 @@ public class OpenWebSocketCounter {
new AtomicInteger(0));
}
private static Timer buildTimer(final String timerName, final String clientPlatformName, final Tags tags) {
return Timer.builder(timerName)
.publishPercentileHistogram(true)
.tags(tags.and(Tag.of(UserAgentTagUtil.PLATFORM_TAG, clientPlatformName)))
.register(Metrics.globalRegistry);
}
public void countOpenWebSocket(final WebSocketSessionContext context) {
final Timer.Sample sample = Timer.start();
// We have to jump through some hoops here to have something "effectively final" for the close listener, but
// assignable from a `catch` block.
final AtomicInteger openWebSocketCounter;
final Timer durationTimer;
{
AtomicInteger calculatedOpenWebSocketCounter;
Timer calculatedDurationTimer;
try {
final ClientPlatform clientPlatform =
UserAgentUtil.parseUserAgentString(context.getClient().getUserAgent()).platform();
calculatedOpenWebSocketCounter = openWebsocketsByClientPlatform.get(clientPlatform);
calculatedDurationTimer = durationTimersByClientPlatform.get(clientPlatform);
} catch (final UnrecognizedUserAgentException e) {
calculatedOpenWebSocketCounter = openWebsocketsFromUnknownPlatforms;
calculatedDurationTimer = durationTimerForUnknownPlatforms;
}
openWebSocketCounter = calculatedOpenWebSocketCounter;
durationTimer = calculatedDurationTimer;
}
openWebSocketCounter.incrementAndGet();
final Tags tagsWithClientPlatform = tags.and(UserAgentTagUtil.getPlatformTag(context.getClient().getUserAgent()));
Metrics.counter(newConnectionCounterName, tagsWithClientPlatform).increment();
context.addWebsocketClosedListener((context1, statusCode, reason) -> {
sample.stop(durationTimer);
sample.stop(Timer.builder(durationTimerName)
.publishPercentileHistogram(true)
.tags(tagsWithClientPlatform)
.register(Metrics.globalRegistry));
openWebSocketCounter.decrementAndGet();
Metrics.counter(WEBSOCKET_CLOSED_COUNTER_NAME, "status", String.valueOf(statusCode))
Metrics.counter(WEBSOCKET_CLOSED_COUNTER_NAME, tagsWithClientPlatform.and("status", String.valueOf(statusCode)))
.increment();
});
}

View File

@@ -33,6 +33,7 @@ import reactor.core.scheduler.Scheduler;
public class AuthenticatedConnectListener implements WebSocketConnectListener {
private static final String OPEN_WEBSOCKET_GAUGE_NAME = name(WebSocketConnection.class, "openWebsockets");
private static final String NEW_CONNECTION_COUNTER_NAME = name(AuthenticatedConnectListener.class, "newConnections");
private static final String CONNECTED_DURATION_TIMER_NAME =
name(AuthenticatedConnectListener.class, "connectedDuration");
@@ -84,10 +85,10 @@ public class AuthenticatedConnectListener implements WebSocketConnectListener {
this.experimentEnrollmentManager = experimentEnrollmentManager;
openAuthenticatedWebSocketCounter =
new OpenWebSocketCounter(OPEN_WEBSOCKET_GAUGE_NAME, CONNECTED_DURATION_TIMER_NAME, Tags.of(AUTHENTICATED_TAG_NAME, "true"));
new OpenWebSocketCounter(OPEN_WEBSOCKET_GAUGE_NAME, NEW_CONNECTION_COUNTER_NAME, CONNECTED_DURATION_TIMER_NAME, Tags.of(AUTHENTICATED_TAG_NAME, "true"));
openUnauthenticatedWebSocketCounter =
new OpenWebSocketCounter(OPEN_WEBSOCKET_GAUGE_NAME, CONNECTED_DURATION_TIMER_NAME, Tags.of(AUTHENTICATED_TAG_NAME, "false"));
new OpenWebSocketCounter(OPEN_WEBSOCKET_GAUGE_NAME, NEW_CONNECTION_COUNTER_NAME, CONNECTED_DURATION_TIMER_NAME, Tags.of(AUTHENTICATED_TAG_NAME, "false"));
}
@Override

View File

@@ -54,6 +54,7 @@ public class ProvisioningConnectListener implements WebSocketConnectListener {
this.timeoutExecutor = timeoutExecutor;
this.timeout = timeout;
this.openWebSocketCounter = new OpenWebSocketCounter(MetricsUtil.name(getClass(), "openWebsockets"),
MetricsUtil.name(getClass(), "newConnections"),
MetricsUtil.name(getClass(), "sessionDuration"));
}