package com.linkedin.thirdeye.anomaly.alert.grouping.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.linkedin.thirdeye.datalayer.dto.GroupedAnomalyResultsDTO;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This filter check if the given grouped anomaly has a size exceeds a certain threshold. The threshold could be
* overridden for different groups; for example, users could specify that the default threshold 3 and it overridden to
* 4 when group name (dimension name) is "country".
*/
public class SizeSeverityAlertGroupFilter extends BaseAlertGroupFilter {
private static final Logger LOG = LoggerFactory.getLogger(SizeSeverityAlertGroupFilter.class);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
public static final String THRESHOLD_KEY = "threshold";
// Override threshold to different dimension map
public static final String OVERRIDE_THRESHOLD_KEY = "overrideThreshold";
private static final int DEFAULT_THRESHOLD = 3;
private int threshold = 3;
private Map<Set<String>, Integer> overrideThreshold = new HashMap<>();
// Getters is limited in package level for testing purpose
int getThreshold() {
return threshold;
}
Map<Set<String>, Integer> getOverrideThreshold() {
return overrideThreshold;
}
@Override
public void setParameters(Map<String, String> props) {
super.setParameters(props);
// Initialize threshold from users' setting
threshold = DEFAULT_THRESHOLD;
if (props.containsKey(THRESHOLD_KEY)) {
threshold = Integer.parseInt(props.get(THRESHOLD_KEY));
}
// Initialize the lookup table for overriding thresholds
if (props.containsKey(OVERRIDE_THRESHOLD_KEY)) {
String overrideJsonPayLoad = props.get(OVERRIDE_THRESHOLD_KEY);
try {
Map<String, Integer> rawOverrideThresholdMap = OBJECT_MAPPER.readValue(overrideJsonPayLoad, HashMap.class);
for (Map.Entry<String, Integer> overrideThresholdEntry : rawOverrideThresholdMap.entrySet()) {
String[] dimensionNames = overrideThresholdEntry.getKey().split(",");
Set<String> dimensionNameSet = new HashSet<>();
for (String dimensionName : dimensionNames) {
dimensionNameSet.add(dimensionName.trim());
}
Integer threshold = overrideThresholdEntry.getValue();
overrideThreshold.put(dimensionNameSet, threshold);
}
} catch (IOException e) {
LOG.error("Failed to reconstruct override threshold mappings from this json string: {}", overrideJsonPayLoad);
}
}
}
@Override
public boolean isQualified(GroupedAnomalyResultsDTO groupedAnomaly) {
Set<String> dimensionNames = new HashSet<>();
dimensionNames.addAll(groupedAnomaly.getDimensions().keySet());
int threshold = this.threshold;
if (overrideThreshold.containsKey(dimensionNames)) {
threshold = overrideThreshold.get(dimensionNames);
}
return CollectionUtils.size(groupedAnomaly.getAnomalyResults()) > threshold;
}
}