use of com.linkedin.thirdeye.datalayer.dto.MergedAnomalyResultDTO in project pinot by linkedin.
the class AnomalyReportGenerator method buildReport.
public void buildReport(List<MergedAnomalyResultDTO> anomalies, ThirdEyeAnomalyConfiguration configuration, AlertConfigDTO alertConfig) {
String subject = "Thirdeye anomaly report : " + alertConfig.getName();
long startTime = System.currentTimeMillis();
long endTime = 0;
for (MergedAnomalyResultDTO anomaly : anomalies) {
if (anomaly.getStartTime() < startTime) {
startTime = anomaly.getStartTime();
}
if (anomaly.getEndTime() > endTime) {
endTime = anomaly.getEndTime();
}
}
buildReport(startTime, endTime, anomalies, subject, configuration, false, alertConfig.getRecipients(), alertConfig.getFromAddress(), alertConfig.getName(), false);
}
use of com.linkedin.thirdeye.datalayer.dto.MergedAnomalyResultDTO in project pinot by linkedin.
the class AnomalyReportGenerator method buildReport.
public void buildReport(long startTime, long endTime, List<MergedAnomalyResultDTO> anomalies, String subject, ThirdEyeAnomalyConfiguration configuration, boolean includeSentAnomaliesOnly, String emailRecipients, String fromEmail, String alertConfigName, boolean includeSummary) {
if (anomalies == null || anomalies.size() == 0) {
LOG.info("No anomalies found to send email, please check the parameters.. exiting");
} else {
Set<String> metrics = new HashSet<>();
int alertedAnomalies = 0;
int feedbackCollected = 0;
int trueAlert = 0;
int falseAlert = 0;
int nonActionable = 0;
List<AnomalyReportDTO> anomalyReportDTOList = new ArrayList<>();
List<String> anomalyIds = new ArrayList<>();
for (MergedAnomalyResultDTO anomaly : anomalies) {
metrics.add(anomaly.getMetric());
if (anomaly.getFeedback() != null) {
feedbackCollected++;
if (anomaly.getFeedback().getFeedbackType().equals(AnomalyFeedbackType.ANOMALY)) {
trueAlert++;
} else if (anomaly.getFeedback().getFeedbackType().equals(AnomalyFeedbackType.NOT_ANOMALY)) {
falseAlert++;
} else {
nonActionable++;
}
}
String feedbackVal = getFeedback(anomaly.getFeedback() == null ? "Not Resolved" : "Resolved(" + anomaly.getFeedback().getFeedbackType().name() + ")");
DateTimeZone dateTimeZone = Utils.getDataTimeZone(anomaly.getCollection());
AnomalyReportDTO anomalyReportDTO = new AnomalyReportDTO(String.valueOf(anomaly.getId()), getAnomalyURL(anomaly, configuration.getDashboardHost()), String.format("%.2f", anomaly.getAvgBaselineVal()), String.format("%.2f", anomaly.getAvgCurrentVal()), getDimensionsString(anomaly.getDimensions()), String.format("%.2f hours (%s to %s) %s", getTimeDiffInHours(anomaly.getStartTime(), anomaly.getEndTime()), getDateString(anomaly.getStartTime(), dateTimeZone), getDateString(anomaly.getEndTime(), dateTimeZone), // duration
getTimezoneString(dateTimeZone)), feedbackVal, anomaly.getFunction().getFunctionName(), // lift
String.format("%+.2f%%", anomaly.getWeight() * 100), getLiftDirection(anomaly.getWeight()), anomaly.getMetric());
if (anomaly.isNotified()) {
alertedAnomalies++;
}
// include notified alerts only in the email
if (includeSentAnomaliesOnly) {
if (anomaly.isNotified()) {
anomalyReportDTOList.add(anomalyReportDTO);
anomalyIds.add(anomalyReportDTO.getAnomalyId());
}
} else {
anomalyReportDTOList.add(anomalyReportDTO);
anomalyIds.add(anomalyReportDTO.getAnomalyId());
}
}
HtmlEmail email = new HtmlEmail();
DateTimeZone timeZone = DateTimeZone.forTimeZone(AlertTaskRunnerV2.DEFAULT_TIME_ZONE);
DataReportHelper.DateFormatMethod dateFormatMethod = new DataReportHelper.DateFormatMethod(timeZone);
Map<String, Object> templateData = new HashMap<>();
templateData.put("timeZone", timeZone);
templateData.put("dateFormat", dateFormatMethod);
templateData.put("startTime", startTime);
templateData.put("endTime", endTime);
templateData.put("anomalyCount", anomalies.size());
templateData.put("metricsCount", metrics.size());
templateData.put("notifiedCount", alertedAnomalies);
templateData.put("feedbackCount", feedbackCollected);
templateData.put("trueAlertCount", trueAlert);
templateData.put("falseAlertCount", falseAlert);
templateData.put("nonActionableCount", nonActionable);
templateData.put("anomalyDetails", anomalyReportDTOList);
templateData.put("alertConfigName", alertConfigName);
templateData.put("includeSummary", includeSummary);
templateData.put("reportGenerationTimeMillis", System.currentTimeMillis());
templateData.put("dashboardHost", configuration.getDashboardHost());
templateData.put("anomalyIds", Joiner.on(",").join(anomalyIds));
boolean isSingleAnomalyEmail = false;
String imgPath = null;
if (anomalyReportDTOList.size() == 1) {
isSingleAnomalyEmail = true;
AnomalyReportDTO singleAnomaly = anomalyReportDTOList.get(0);
subject = subject + " - " + singleAnomaly.getMetric();
imgPath = EmailScreenshotHelper.takeGraphScreenShot(singleAnomaly.getAnomalyId(), configuration);
String cid = "";
try {
cid = email.embed(new File(imgPath));
} catch (Exception e) {
LOG.error("Exception while embedding screenshot for anomaly {}", singleAnomaly.getAnomalyId(), e);
}
templateData.put("cid", cid);
}
buildEmailTemplateAndSendAlert(templateData, configuration.getSmtpConfiguration(), subject, emailRecipients, fromEmail, isSingleAnomalyEmail, email);
if (StringUtils.isNotBlank(imgPath)) {
try {
Files.deleteIfExists(new File(imgPath).toPath());
} catch (IOException e) {
LOG.error("Exception in deleting screenshot {}", imgPath, e);
}
}
}
}
use of com.linkedin.thirdeye.datalayer.dto.MergedAnomalyResultDTO in project pinot by linkedin.
the class AnomalyReportGenerator method getAnomaliesForMetrics.
public List<MergedAnomalyResultDTO> getAnomaliesForMetrics(List<String> metrics, long startTime, long endTime) {
List<MergedAnomalyResultDTO> anomalies = new ArrayList<>();
LOG.info("fetching anomalies for metrics : " + metrics);
for (String metric : metrics) {
List<MetricConfigDTO> metricConfigDTOList = metricConfigManager.findByMetricName(metric);
for (MetricConfigDTO metricConfigDTO : metricConfigDTOList) {
List<MergedAnomalyResultDTO> results = anomalyResultManager.findByCollectionMetricTime(metricConfigDTO.getDataset(), metric, startTime, endTime, false);
LOG.info("Found {} result for metric {}", results.size(), metric);
anomalies.addAll(results);
}
}
return anomalies;
}
use of com.linkedin.thirdeye.datalayer.dto.MergedAnomalyResultDTO in project pinot by linkedin.
the class AlertTaskRunnerV2 method sendAnomalyReport.
private void sendAnomalyReport() throws Exception {
AlertConfigBean.EmailConfig emailConfig = alertConfig.getEmailConfig();
if (emailConfig != null && emailConfig.getFunctionIds() != null) {
List<Long> functionIds = alertConfig.getEmailConfig().getFunctionIds();
List<MergedAnomalyResultDTO> mergedAnomaliesAllResults = new ArrayList<>();
long lastNotifiedAnomaly = emailConfig.getAnomalyWatermark();
for (Long functionId : functionIds) {
List<MergedAnomalyResultDTO> resultsForFunction = anomalyMergedResultDAO.findByFunctionIdAndIdGreaterThan(functionId, lastNotifiedAnomaly);
if (resultsForFunction != null && resultsForFunction.size() > 0) {
mergedAnomaliesAllResults.addAll(resultsForFunction);
}
// fetch anomalies having id lesser than the watermark for the same function with notified = false & endTime > last one day
// these anomalies are the ones that did not qualify filtration rule and got modified.
// We should add these again so that these could be included in email if qualified through filtration rule
List<MergedAnomalyResultDTO> filteredAnomalies = anomalyMergedResultDAO.findUnNotifiedByFunctionIdAndIdLesserThanAndEndTimeGreaterThanLastOneDay(functionId, lastNotifiedAnomaly);
if (filteredAnomalies != null && filteredAnomalies.size() > 0) {
mergedAnomaliesAllResults.addAll(filteredAnomalies);
}
}
// apply filtration rule
List<MergedAnomalyResultDTO> results = AlertFilterHelper.applyFiltrationRule(mergedAnomaliesAllResults, alertFilterFactory);
if (results.isEmpty()) {
LOG.info("Zero anomalies found, skipping sending email");
} else {
AnomalyReportGenerator.getInstance().buildReport(results, thirdeyeConfig, alertConfig);
updateNotifiedStatus(results);
// update anomaly watermark in alertConfig
long lastNotifiedAlertId = emailConfig.getAnomalyWatermark();
for (MergedAnomalyResultDTO anomalyResult : results) {
if (anomalyResult.getId() > lastNotifiedAlertId) {
lastNotifiedAlertId = anomalyResult.getId();
}
}
if (lastNotifiedAlertId != emailConfig.getAnomalyWatermark()) {
alertConfig.getEmailConfig().setAnomalyWatermark(lastNotifiedAlertId);
alertConfigDAO.update(alertConfig);
}
}
}
}
use of com.linkedin.thirdeye.datalayer.dto.MergedAnomalyResultDTO in project pinot by linkedin.
the class AlertTaskRunnerV2 method updateNotifiedStatus.
private void updateNotifiedStatus(List<MergedAnomalyResultDTO> mergedResults) {
for (MergedAnomalyResultDTO mergedResult : mergedResults) {
mergedResult.setNotified(true);
anomalyMergedResultDAO.update(mergedResult);
}
}
Aggregations