Avoid unnecessary re-parsing of User-Agent strings

This commit is contained in:
Jon Chambers
2026-02-18 10:14:22 -05:00
committed by Jon Chambers
parent d659af566b
commit 5ffcdb1c3f
3 changed files with 62 additions and 22 deletions

View File

@@ -31,6 +31,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.storage.ClientReleaseManager;
import org.whispersystems.textsecuregcm.util.logging.UriInfoUtil;
import org.whispersystems.textsecuregcm.util.ua.UnrecognizedUserAgentException;
import org.whispersystems.textsecuregcm.util.ua.UserAgent;
import org.whispersystems.textsecuregcm.util.ua.UserAgentUtil;
/**
* Gathers and reports HTTP request metrics at the Jetty container level, which sits above Jersey. In order to get
@@ -49,7 +52,6 @@ public class MetricsHttpChannelListener implements HttpChannel.Listener, Contain
private static final Logger logger = LoggerFactory.getLogger(MetricsHttpChannelListener.class);
private record RequestInfo(String path, String method, int statusCode, @Nullable String userAgent) {
}
private final ClientReleaseManager clientReleaseManager;
@@ -136,15 +138,30 @@ public class MetricsHttpChannelListener implements HttpChannel.Listener, Contain
final RequestInfo requestInfo = getRequestInfo(request);
@Nullable final UserAgent userAgent;
{
UserAgent parsedUserAgent;
try {
parsedUserAgent = UserAgentUtil.parseUserAgentString(requestInfo.userAgent());
} catch (final UnrecognizedUserAgentException e) {
parsedUserAgent = null;
}
userAgent = parsedUserAgent;
}
final Tag platformTag = UserAgentTagUtil.getPlatformTag(userAgent);
final List<Tag> tags = new ArrayList<>(5);
tags.add(Tag.of(PATH_TAG, requestInfo.path()));
tags.add(Tag.of(METHOD_TAG, requestInfo.method()));
tags.add(Tag.of(STATUS_CODE_TAG, String.valueOf(requestInfo.statusCode())));
tags.add(Tag.of(TRAFFIC_SOURCE_TAG, TrafficSource.HTTP.name().toLowerCase()));
tags.add(UserAgentTagUtil.getPlatformTag(requestInfo.userAgent()));
tags.add(platformTag);
final Optional<Tag> maybeClientVersionTag =
UserAgentTagUtil.getClientVersionTag(requestInfo.userAgent, clientReleaseManager);
UserAgentTagUtil.getClientVersionTag(userAgent, clientReleaseManager);
maybeClientVersionTag.ifPresent(tags::add);
@@ -154,7 +171,7 @@ public class MetricsHttpChannelListener implements HttpChannel.Listener, Contain
meterRegistry.counter(REQUEST_BYTES_COUNTER_NAME, tags).increment(request.getContentRead());
maybeClientVersionTag.ifPresent(clientVersionTag -> meterRegistry.counter(REQUESTS_BY_VERSION_COUNTER_NAME,
Tags.of(clientVersionTag, UserAgentTagUtil.getPlatformTag(requestInfo.userAgent)))
Tags.of(clientVersionTag, platformTag))
.increment());
}

View File

@@ -24,6 +24,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.storage.ClientReleaseManager;
import org.whispersystems.textsecuregcm.util.logging.UriInfoUtil;
import org.whispersystems.textsecuregcm.util.ua.UnrecognizedUserAgentException;
import org.whispersystems.textsecuregcm.util.ua.UserAgent;
import org.whispersystems.textsecuregcm.util.ua.UserAgentUtil;
import org.whispersystems.websocket.WebSocketResourceProvider;
/**
@@ -84,6 +87,24 @@ public class MetricsRequestEventListener implements RequestEventListener {
public void onEvent(final RequestEvent event) {
if (event.getType() == RequestEvent.Type.FINISHED) {
if (!event.getUriInfo().getMatchedTemplates().isEmpty()) {
@Nullable final UserAgent userAgent;
{
final List<String> userAgentValues = event.getContainerRequest().getRequestHeader(HttpHeaders.USER_AGENT);
final String userAgentString = userAgentValues != null && !userAgentValues.isEmpty() ? userAgentValues.getFirst() : null;
UserAgent parsedUserAgent;
try {
parsedUserAgent = UserAgentUtil.parseUserAgentString(userAgentString);
} catch (final UnrecognizedUserAgentException e) {
parsedUserAgent = null;
}
userAgent = parsedUserAgent;
}
final Tag platformTag = UserAgentTagUtil.getPlatformTag(userAgent);
final List<Tag> tags = new ArrayList<>();
tags.add(Tag.of(PATH_TAG, UriInfoUtil.getPathTemplate(event.getUriInfo())));
tags.add(Tag.of(METHOD_TAG, event.getContainerRequest().getMethod()));
@@ -98,14 +119,7 @@ public class MetricsRequestEventListener implements RequestEventListener {
.map(Optional::isPresent)
.orElse(false)
.toString()));
@Nullable final String userAgent;
{
final List<String> userAgentValues = event.getContainerRequest().getRequestHeader(HttpHeaders.USER_AGENT);
userAgent = userAgentValues != null && !userAgentValues.isEmpty() ? userAgentValues.getFirst() : null;
}
tags.add(UserAgentTagUtil.getPlatformTag(userAgent));
tags.add(platformTag);
final Optional<Tag> maybeClientVersionTag =
UserAgentTagUtil.getClientVersionTag(userAgent, clientReleaseManager);
@@ -132,7 +146,7 @@ public class MetricsRequestEventListener implements RequestEventListener {
.ifPresent(bytes -> meterRegistry.counter(RESPONSE_BYTES_COUNTER_NAME, tags).increment(bytes));
maybeClientVersionTag.ifPresent(clientVersionTag -> meterRegistry.counter(REQUESTS_BY_VERSION_COUNTER_NAME,
Tags.of(clientVersionTag, UserAgentTagUtil.getPlatformTag(userAgent)))
Tags.of(clientVersionTag, platformTag))
.increment());
}
}

View File

@@ -35,7 +35,7 @@ public class UserAgentTagUtil {
return getPlatformTag(containerRequestContext.getHeaderString(HttpHeaders.USER_AGENT));
}
public static Tag getPlatformTag(final String userAgentString) {
public static Tag getPlatformTag(@Nullable final String userAgentString) {
if (SERVER_UA.equals(userAgentString)) {
return Tag.of(PLATFORM_TAG, "server");
@@ -55,16 +55,25 @@ public class UserAgentTagUtil {
return Tag.of(PLATFORM_TAG, userAgent != null ? userAgent.platform().name().toLowerCase() : "unrecognized");
}
public static Optional<Tag> getClientVersionTag(final String userAgentString, final ClientReleaseManager clientReleaseManager) {
try {
final UserAgent userAgent = UserAgentUtil.parseUserAgentString(userAgentString);
public static Optional<Tag> getClientVersionTag(@Nullable final String userAgentString,
final ClientReleaseManager clientReleaseManager) {
if (clientReleaseManager.isVersionActive(userAgent.platform(), userAgent.version())) {
return Optional.of(Tag.of(VERSION_TAG, userAgent.version().toString()));
}
} catch (final UnrecognizedUserAgentException ignored) {
try {
return getClientVersionTag(UserAgentUtil.parseUserAgentString(userAgentString), clientReleaseManager);
} catch (final UnrecognizedUserAgentException e) {
return Optional.empty();
}
}
public static Optional<Tag> getClientVersionTag(@Nullable final UserAgent userAgent,
final ClientReleaseManager clientReleaseManager) {
if (userAgent == null) {
return Optional.empty();
}
return Optional.empty();
return clientReleaseManager.isVersionActive(userAgent.platform(), userAgent.version())
? Optional.of(Tag.of(VERSION_TAG, userAgent.version().toString()))
: Optional.empty();
}
}