Move UA tag extraction into its own utility class.

This commit is contained in:
Jon Chambers
2020-05-21 10:03:43 -04:00
committed by Jon Chambers
parent eede4e50ca
commit 9ba5ee8043
4 changed files with 98 additions and 72 deletions

View File

@@ -8,36 +8,21 @@ import io.micrometer.core.instrument.Tag;
import org.glassfish.jersey.server.ExtendedUriInfo;
import org.glassfish.jersey.server.monitoring.RequestEvent;
import org.glassfish.jersey.server.monitoring.RequestEventListener;
import org.whispersystems.textsecuregcm.util.Pair;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Gathers and reports request-level metrics.
*/
class MetricsRequestEventListener implements RequestEventListener {
static final int MAX_VERSIONS = 10_000;
static final String COUNTER_NAME = MetricRegistry.name(MetricsRequestEventListener.class, "request");
static final String COUNTER_NAME = MetricRegistry.name(MetricsRequestEventListener.class, "request");
static final String PATH_TAG = "path";
static final String STATUS_CODE_TAG = "status";
static final String PATH_TAG = "path";
static final String STATUS_CODE_TAG = "status";
static final String PLATFORM_TAG = "platform";
static final String VERSION_TAG = "clientVersion";
static final List<Tag> OVERFLOW_TAGS = List.of(Tag.of(PLATFORM_TAG, "overflow"), Tag.of(VERSION_TAG, "overflow"));
static final List<Tag> UNRECOGNIZED_TAGS = List.of(Tag.of(PLATFORM_TAG, "unrecognized"), Tag.of(VERSION_TAG, "unrecognized"));
private static final Pattern USER_AGENT_PATTERN = Pattern.compile("Signal-([^ ]+) ([^ ]+).*$", Pattern.CASE_INSENSITIVE);
private final MeterRegistry meterRegistry;
private final Set<Pair<String, String>> seenVersions = new HashSet<>();
private final MeterRegistry meterRegistry;
public MetricsRequestEventListener() {
this(Metrics.globalRegistry);
@@ -59,7 +44,7 @@ class MetricsRequestEventListener implements RequestEventListener {
event.getContainerRequest().getRequestHeader("User-Agent")
.stream()
.findFirst()
.map(this::getUserAgentTags)
.map(UserAgentTagUtil::getUserAgentTags)
.ifPresent(tags::addAll);
meterRegistry.counter(COUNTER_NAME, tags).increment();
@@ -77,26 +62,4 @@ class MetricsRequestEventListener implements RequestEventListener {
return pathBuilder.toString();
}
@VisibleForTesting
List<Tag> getUserAgentTags(final String userAgent) {
final Matcher matcher = USER_AGENT_PATTERN.matcher(userAgent);
final List<Tag> tags;
if (matcher.matches()) {
final Pair<String, String> platformAndVersion = new Pair<>(matcher.group(1).toLowerCase(), matcher.group(2));
final boolean allowVersion;
synchronized (seenVersions) {
allowVersion = seenVersions.contains(platformAndVersion) || (seenVersions.size() < MAX_VERSIONS && seenVersions.add(platformAndVersion));
}
tags = allowVersion ? List.of(Tag.of(PLATFORM_TAG, platformAndVersion.first()), Tag.of(VERSION_TAG, platformAndVersion.second())) : OVERFLOW_TAGS;
} else {
tags = UNRECOGNIZED_TAGS;
}
return tags;
}
}

View File

@@ -0,0 +1,52 @@
package org.whispersystems.textsecuregcm.metrics;
import io.micrometer.core.instrument.Tag;
import org.whispersystems.textsecuregcm.util.Pair;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility class for extracting platform/version metrics tags from User-Agent strings.
*/
public class UserAgentTagUtil {
static final int MAX_VERSIONS = 10_000;
public static final String PLATFORM_TAG = "platform";
public static final String VERSION_TAG = "clientVersion";
static final List<Tag> OVERFLOW_TAGS = List.of(Tag.of(PLATFORM_TAG, "overflow"), Tag.of(VERSION_TAG, "overflow"));
static final List<Tag> UNRECOGNIZED_TAGS = List.of(Tag.of(PLATFORM_TAG, "unrecognized"), Tag.of(VERSION_TAG, "unrecognized"));
private static final Pattern USER_AGENT_PATTERN = Pattern.compile("Signal-([^ ]+) ([^ ]+).*$", Pattern.CASE_INSENSITIVE);
private static final Set<Pair<String, String>> SEEN_VERSIONS = new HashSet<>();
private UserAgentTagUtil() {
}
public static List<Tag> getUserAgentTags(final String userAgent) {
final Matcher matcher = USER_AGENT_PATTERN.matcher(userAgent);
final List<Tag> tags;
if (matcher.matches()) {
final Pair<String, String> platformAndVersion = new Pair<>(matcher.group(1).toLowerCase(), matcher.group(2));
final boolean allowVersion;
synchronized (SEEN_VERSIONS) {
allowVersion = SEEN_VERSIONS.contains(platformAndVersion) || (SEEN_VERSIONS.size() < MAX_VERSIONS && SEEN_VERSIONS.add(platformAndVersion));
}
tags = allowVersion ? List.of(Tag.of(PLATFORM_TAG, platformAndVersion.first()), Tag.of(VERSION_TAG, platformAndVersion.second())) : OVERFLOW_TAGS;
} else {
tags = UNRECOGNIZED_TAGS;
}
return tags;
}
}