package com.etsy.statsd.profiler.util; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Utility class for filtering stack traces * Assumes a string representation like that produced by @link{StackTraceFormatter} * * @author Andrew Johnson */ public class StackTraceFilter { public static final Pattern MATCH_EVERYTHING = Pattern.compile("^.*$"); public static final Pattern MATCH_NOTHING = Pattern.compile("$^"); private final Pattern includePattern; private final Pattern excludePattern; // This allows us to shortcut doing regex matching for performance private final boolean arePatternsDefault; public StackTraceFilter(List<String> includePackages, List<String> excludePackages) { includePattern = getPackagePattern(includePackages, MATCH_EVERYTHING); excludePattern = getPackagePattern(excludePackages, MATCH_NOTHING); arePatternsDefault = includePattern == MATCH_EVERYTHING && excludePattern == MATCH_NOTHING; } /** * Indicate if this stack trace should be included in the filter * Checks if the stack trace matches the include pattern and does not match the exclude pattern * * @param formattedStackTrace The stack trace to check for inclusion in the filter * @return True if it should be included, false otherwise */ public boolean includeStackTrace(String formattedStackTrace) { return arePatternsDefault || includeMatches(formattedStackTrace) && !excludeMatches(formattedStackTrace); } /** * Indicate if this stack trace matches one of the included packages * * @param formattedStackTrace The stack trace to check against the included packages * @return True if it matches an included package, false otherwise */ public boolean includeMatches(String formattedStackTrace) { return matches(includePattern, formattedStackTrace); } /** * Indicate if this stack trace matches one of the excluded packages * * @param formattedStackTrace The stack trace to check against the excluded packages * @return True if it matches an excluded package, false otherwise */ public boolean excludeMatches(String formattedStackTrace) { return matches(excludePattern, formattedStackTrace); } /** * Indicate if this stack trace matches the given pattern * * @param pattern The pattern to match against the stack trace * @param formattedStackTrace The stack trace to check against the pattern * @return True if the stack trace matches the pattern, false otherwise */ private static boolean matches(Pattern pattern, String formattedStackTrace) { Matcher matcher = pattern.matcher(formattedStackTrace); return matcher.matches(); } /** * Construct a Pattern that matches any of the given packages * * @param packageWhitelist The packages to match in this Pattern * @return A Pattern object that matches any of the given packages */ public Pattern getPackagePattern(List<String> packageWhitelist, Pattern defaultPattern) { if (packageWhitelist == null || packageWhitelist.isEmpty()) { return defaultPattern; } else { return Pattern.compile(String.format("(.*\\.|^)(%s).*", Joiner.on("|").join(Lists.transform(packageWhitelist, new Function<String, String>() { @Override public String apply(String s) { return String.format("(%s)", s.replace(".", "-")); } })))); } } }