From 0d9061b8388ff89f4e5d36509a84a82eedd4fb5e Mon Sep 17 00:00:00 2001 From: Brian Murphy Date: Wed, 13 Aug 2014 17:05:25 +0100 Subject: [PATCH] Alerting : better email formatting This commit enables better email formatting. Original commit: elastic/x-pack-elasticsearch@8be3e3b6d1c3c0d5e9100acec1cb0655f8d78e5f --- .../elasticsearch/alerting/AlertManager.java | 82 ++++++++++--------- .../elasticsearch/alerting/AlertResult.java | 19 ++++- .../alerting/AlertScheduler.java | 27 +++--- .../elasticsearch/alerting/AlertTrigger.java | 46 ++++++++++- .../alerting/EmailAlertAction.java | 37 ++++++++- .../alerting/EmailAlertActionFactory.java | 16 ++++ .../alerting/TriggerManager.java | 4 +- 7 files changed, 164 insertions(+), 67 deletions(-) diff --git a/src/main/java/org/elasticsearch/alerting/AlertManager.java b/src/main/java/org/elasticsearch/alerting/AlertManager.java index 38f434208ca..fe11393555d 100644 --- a/src/main/java/org/elasticsearch/alerting/AlertManager.java +++ b/src/main/java/org/elasticsearch/alerting/AlertManager.java @@ -153,51 +153,55 @@ public class AlertManager extends AbstractLifecycleComponent { ).setTypes(ALERT_TYPE).setIndices(ALERT_INDEX).execute().get(); for (SearchHit sh : searchResponse.getHits()) { String alertId = sh.getId(); - logger.warn("Found : [{}]", alertId); - Map fields = sh.sourceAsMap(); - //Map fields = sh.getFields(); - - for (String field : fields.keySet() ) { - logger.warn("Field : [{}]", field); - } - - String query = fields.get(QUERY_FIELD.getPreferredName()).toString(); - String schedule = fields.get(SCHEDULE_FIELD.getPreferredName()).toString(); - Object triggerObj = fields.get(TRIGGER_FIELD.getPreferredName()); - AlertTrigger trigger = null; - if (triggerObj instanceof Map) { - Map triggerMap = (Map) triggerObj; - trigger = TriggerManager.parseTriggerFromMap(triggerMap); - } else { - throw new ElasticsearchException("Unable to parse trigger [" + triggerObj + "]"); - } - - String timeString = fields.get(TIMEPERIOD_FIELD.getPreferredName()).toString(); - TimeValue timePeriod = TimeValue.parseTimeValue(timeString, defaultTimePeriod); - - Object actionObj = fields.get(ACTION_FIELD.getPreferredName()); - List actions = null; - if (actionObj instanceof Map) { - Map actionMap = (Map) actionObj; - actions = actionManager.parseActionsFromMap(actionMap); - } else { - throw new ElasticsearchException("Unable to parse actions [" + triggerObj + "]"); - } - - DateTime lastRan = new DateTime(fields.get("lastRan").toString()); - - List indices = null; - if (fields.get(INDICES.getPreferredName()) != null && fields.get(INDICES.getPreferredName()) instanceof List){ - indices = (List)fields.get(INDICES.getPreferredName()); - } - - Alert alert = new Alert(alertId, query, trigger, timePeriod, actions, schedule, lastRan, indices); + Alert alert = parseAlert(sh, alertId); alertMap.put(alertId, alert); } logger.warn("Loaded [{}] alerts from the alert index.", alertMap.size()); } } + private Alert parseAlert(SearchHit sh, String alertId) { + logger.warn("Found : [{}]", alertId); + Map fields = sh.sourceAsMap(); + //Map fields = sh.getFields(); + + for (String field : fields.keySet() ) { + logger.warn("Field : [{}]", field); + } + + String query = fields.get(QUERY_FIELD.getPreferredName()).toString(); + String schedule = fields.get(SCHEDULE_FIELD.getPreferredName()).toString(); + Object triggerObj = fields.get(TRIGGER_FIELD.getPreferredName()); + AlertTrigger trigger = null; + if (triggerObj instanceof Map) { + Map triggerMap = (Map) triggerObj; + trigger = TriggerManager.parseTriggerFromMap(triggerMap); + } else { + throw new ElasticsearchException("Unable to parse trigger [" + triggerObj + "]"); + } + + String timeString = fields.get(TIMEPERIOD_FIELD.getPreferredName()).toString(); + TimeValue timePeriod = TimeValue.parseTimeValue(timeString, defaultTimePeriod); + + Object actionObj = fields.get(ACTION_FIELD.getPreferredName()); + List actions = null; + if (actionObj instanceof Map) { + Map actionMap = (Map) actionObj; + actions = actionManager.parseActionsFromMap(actionMap); + } else { + throw new ElasticsearchException("Unable to parse actions [" + triggerObj + "]"); + } + + DateTime lastRan = new DateTime(fields.get("lastRan").toString()); + + List indices = null; + if (fields.get(INDICES.getPreferredName()) != null && fields.get(INDICES.getPreferredName()) instanceof List){ + indices = (List)fields.get(INDICES.getPreferredName()); + } + + return new Alert(alertId, query, trigger, timePeriod, actions, schedule, lastRan, indices); + } + public Alert getAlertForName(String alertName) { synchronized (alertMap) { return alertMap.get(alertName); diff --git a/src/main/java/org/elasticsearch/alerting/AlertResult.java b/src/main/java/org/elasticsearch/alerting/AlertResult.java index 547cfafb373..54f48efe695 100644 --- a/src/main/java/org/elasticsearch/alerting/AlertResult.java +++ b/src/main/java/org/elasticsearch/alerting/AlertResult.java @@ -6,11 +6,16 @@ package org.elasticsearch.alerting; import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.xcontent.XContentBuilder; + +import java.util.Arrays; public class AlertResult { public SearchResponse searchResponse; public AlertTrigger trigger; public boolean isTriggered; + public XContentBuilder query; + public String[] indices; @Override public boolean equals(Object o) { @@ -20,17 +25,23 @@ public class AlertResult { AlertResult that = (AlertResult) o; if (isTriggered != that.isTriggered) return false; - if (!searchResponse.equals(that.searchResponse)) return false; - if (!trigger.equals(that.trigger)) return false; + if (!Arrays.equals(indices, that.indices)) return false; + if (query != null ? !query.equals(that.query) : that.query != null) return false; + if (searchResponse != null ? !searchResponse.equals(that.searchResponse) : that.searchResponse != null) + return false; + if (trigger != null ? !trigger.equals(that.trigger) : that.trigger != null) return false; return true; } @Override public int hashCode() { - int result = searchResponse.hashCode(); - result = 31 * result + trigger.hashCode(); + int result = searchResponse != null ? searchResponse.hashCode() : 0; + result = 31 * result + (trigger != null ? trigger.hashCode() : 0); result = 31 * result + (isTriggered ? 1 : 0); + result = 31 * result + (query != null ? query.hashCode() : 0); + result = 31 * result + (indices != null ? Arrays.hashCode(indices) : 0); return result; } + } diff --git a/src/main/java/org/elasticsearch/alerting/AlertScheduler.java b/src/main/java/org/elasticsearch/alerting/AlertScheduler.java index 3a6e50b2498..f5bc42e3dc7 100644 --- a/src/main/java/org/elasticsearch/alerting/AlertScheduler.java +++ b/src/main/java/org/elasticsearch/alerting/AlertScheduler.java @@ -5,21 +5,15 @@ */ package org.elasticsearch.alerting; -import org.apache.lucene.util.BytesRef; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptResponse; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; -import org.elasticsearch.common.ParseField; -import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.joda.time.DateTime; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.*; -import org.elasticsearch.script.CompiledScript; -import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.ScriptService; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; @@ -27,25 +21,21 @@ import org.quartz.simpl.SimpleJobFactory; import java.io.IOException; import java.util.Date; -import java.util.HashMap; -import java.util.Map; public class AlertScheduler extends AbstractLifecycleComponent { Scheduler scheduler = null; private final AlertManager alertManager; private final Client client; - private final ScriptService scriptService; private final TriggerManager triggerManager; private final AlertActionManager actionManager; @Inject - public AlertScheduler(Settings settings, AlertManager alertManager, ScriptService scriptService, Client client, + public AlertScheduler(Settings settings, AlertManager alertManager, Client client, TriggerManager triggerManager, AlertActionManager actionManager) { super(settings); this.alertManager = alertManager; this.client = client; - this.scriptService = scriptService; this.triggerManager = triggerManager; this.actionManager = actionManager; try { @@ -66,18 +56,24 @@ public class AlertScheduler extends AbstractLifecycleComponent { logger.warn("Running the following query : [{}]", builder.string()); SearchRequestBuilder srb = client.prepareSearch().setSource(builder); + String[] indices = alert.indices().toArray(new String[0]); if (alert.indices() != null ){ - srb.setIndices(alert.indices().toArray(new String[0])); + logger.warn("Setting indices to : " + alert.indices()); + srb.setIndices(indices); } SearchResponse sr = srb.execute().get(); - logger.warn("Got search response"); + logger.warn("Got search response hits : [{}]", sr.getHits().getTotalHits() ); AlertResult result = new AlertResult(); + //TODO: move these to ctr result.isTriggered = triggerManager.isTriggered(alertName,sr); - result.searchResponse = sr; + result.trigger = alert.trigger(); + result.query = builder; + result.indices = indices; + if (result.isTriggered) { logger.warn("We have triggered"); - //actionManager.doAction(alertName,result); + actionManager.doAction(alertName,result); logger.warn("Did action !"); }else{ logger.warn("We didn't trigger"); @@ -94,7 +90,6 @@ public class AlertScheduler extends AbstractLifecycleComponent { Date scheduledFireTime = jobExecutionContext.getScheduledFireTime(); DateTime clampEnd = new DateTime(scheduledFireTime); DateTime clampStart = clampEnd.minusSeconds((int)alert.timePeriod().seconds()); - logger.error("Subtracting : [{}] seconds from [{}] = [{}]", (int)alert.timePeriod().seconds(), clampEnd, clampStart ); XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); builder.startObject(); diff --git a/src/main/java/org/elasticsearch/alerting/AlertTrigger.java b/src/main/java/org/elasticsearch/alerting/AlertTrigger.java index 76b0ec99fd4..ca962d29b01 100644 --- a/src/main/java/org/elasticsearch/alerting/AlertTrigger.java +++ b/src/main/java/org/elasticsearch/alerting/AlertTrigger.java @@ -46,6 +46,10 @@ public class AlertTrigger { this.value = value; } + public String toString(){ + return triggerType + " " + trigger + " " + value; + } + public static enum SimpleTrigger { EQUAL, NOT_EQUAL, @@ -54,8 +58,8 @@ public class AlertTrigger { RISES_BY, FALLS_BY; - public static SimpleTrigger fromString(final String sAction) { - switch (sAction) { + public static SimpleTrigger fromString(final String sTrigger) { + switch (sTrigger) { case ">": return GREATER_THAN; case "<": @@ -70,9 +74,32 @@ public class AlertTrigger { case "<-": return FALLS_BY; default: - throw new ElasticsearchIllegalArgumentException("Unknown AlertAction:SimpleAction [" + sAction + "]"); + throw new ElasticsearchIllegalArgumentException("Unknown AlertAction:SimpleAction [" + sTrigger + "]"); } } + + public static String asString(final SimpleTrigger trigger){ + switch (trigger) { + case GREATER_THAN: + return ">"; + case LESS_THAN: + return "<"; + case EQUAL: + return "=="; + case NOT_EQUAL: + return "!="; + case RISES_BY: + return "->"; + case FALLS_BY: + return "<-"; + default: + return "?"; + } + } + + public String toString(){ + return asString(this); + } } public static enum TriggerType { @@ -86,6 +113,19 @@ public class AlertTrigger { throw new ElasticsearchIllegalArgumentException("Unknown AlertTrigger:TriggerType [" + sTriggerType + "]"); } } + + public static String asString(final TriggerType triggerType){ + switch (triggerType) { + case NUMBER_OF_EVENTS: + return "numberOfEvents"; + default: + return "unknown"; + } + } + + public String toString(){ + return asString(this); + } } } diff --git a/src/main/java/org/elasticsearch/alerting/EmailAlertAction.java b/src/main/java/org/elasticsearch/alerting/EmailAlertAction.java index 49684c33e96..0735a51f695 100644 --- a/src/main/java/org/elasticsearch/alerting/EmailAlertAction.java +++ b/src/main/java/org/elasticsearch/alerting/EmailAlertAction.java @@ -6,10 +6,13 @@ package org.elasticsearch.alerting; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHitField; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Properties; import javax.mail.*; import javax.mail.internet.AddressException; @@ -18,6 +21,7 @@ import javax.mail.internet.MimeMessage; public class EmailAlertAction implements AlertAction { List
emailAddresses = new ArrayList<>(); + String displayField = null; String from = "esalertingtest@gmail.com"; String passwd = "elasticsearchforthewin"; @@ -39,6 +43,10 @@ public class EmailAlertAction implements AlertAction { } } + public void displayField(String displayField){ + this.displayField = displayField; + } + @Override public boolean doAction(String alertName, AlertResult result) { Properties props = new Properties(); @@ -57,13 +65,36 @@ public class EmailAlertAction implements AlertAction { message.setFrom(new InternetAddress(from)); message.setRecipients(Message.RecipientType.TO, emailAddresses.toArray(new Address[1])); - message.setSubject("Elasticsearch Alert from " + alertName); - message.setText(result.searchResponse.toString()); + message.setSubject("Elasticsearch Alert " + alertName + " triggered"); + StringBuffer output = new StringBuffer(); + output.append("The following query triggered because " + result.trigger.toString() + "\n"); + output.append("The total number of hits returned : " + result.searchResponse.getHits().getTotalHits() + "\n"); + output.append("For query : " + XContentHelper.convertToJson(result.query.bytes(),true,true) + "\n"); + output.append("\n"); + output.append("Indices : "); + for (String index : result.indices) { + output.append(index); + output.append("/"); + } + output.append("\n"); + output.append("\n"); + if (displayField != null) { + for (SearchHit sh : result.searchResponse.getHits().getHits()) { + if (sh.sourceAsMap().containsKey(displayField)) { + output.append(sh.sourceAsMap().get(displayField).toString()); + } else { + output.append(new String(sh.source())); + } + output.append("\n"); + } + } else { + output.append(result.searchResponse.toString()); + } + message.setText(output.toString()); Transport.send(message); } catch (Exception e){ throw new ElasticsearchException("Failed to send mail", e); } - return true; } } diff --git a/src/main/java/org/elasticsearch/alerting/EmailAlertActionFactory.java b/src/main/java/org/elasticsearch/alerting/EmailAlertActionFactory.java index 8bd16bbac6a..6346a85faeb 100644 --- a/src/main/java/org/elasticsearch/alerting/EmailAlertActionFactory.java +++ b/src/main/java/org/elasticsearch/alerting/EmailAlertActionFactory.java @@ -5,7 +5,10 @@ */ package org.elasticsearch.alerting; +import org.elasticsearch.ElasticsearchException; + import java.util.List; +import java.util.Map; public class EmailAlertActionFactory implements AlertActionFactory{ @@ -16,6 +19,19 @@ public class EmailAlertActionFactory implements AlertActionFactory{ for (String emailAddress : (List)parameters) { action.addEmailAddress(emailAddress); } + } else if (parameters instanceof Map) { + Map paramMap = (Map)parameters; + Object addresses = paramMap.get("addresses"); + if (addresses == null){ + throw new ElasticsearchException("Unable to parse email addresses from : " + parameters); + } + for (String emailAddress : (List)addresses) { + action.addEmailAddress(emailAddress); + } + Object displayField = paramMap.get("display"); + if (displayField != null){ + action.displayField(displayField.toString()); + } } return action; } diff --git a/src/main/java/org/elasticsearch/alerting/TriggerManager.java b/src/main/java/org/elasticsearch/alerting/TriggerManager.java index a530a33eaa6..4cc57f84a6b 100644 --- a/src/main/java/org/elasticsearch/alerting/TriggerManager.java +++ b/src/main/java/org/elasticsearch/alerting/TriggerManager.java @@ -41,10 +41,10 @@ public class TriggerManager extends AbstractComponent { logger.warn("Could not find alert named [{}] in alert manager perhaps it has been deleted.", alertName); return false; } - int testValue; + long testValue; switch (alert.trigger().triggerType()) { case NUMBER_OF_EVENTS: - testValue = response.getHits().getHits().length; + testValue = response.getHits().getTotalHits(); break; default: throw new ElasticsearchIllegalArgumentException("Bad value for trigger.triggerType [" + alert.trigger().triggerType() + "]");