Skip to content

Commit

Permalink
0003805: Added notification templates
Browse files Browse the repository at this point in the history
  • Loading branch information
evan-miller-jumpmind committed Apr 25, 2022
1 parent 7bcd4d6 commit a02832c
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 84 deletions.
Expand Up @@ -20,10 +20,18 @@
*/
package org.jumpmind.symmetric.model;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.jumpmind.symmetric.common.ParameterConstants;

import com.google.gson.Gson;

public class Notification implements IModelObject {
private static final long serialVersionUID = 1L;
private String notificationId;
Expand Down Expand Up @@ -114,10 +122,36 @@ public String getExpression() {
return expression;
}

public LogExpression getLogExpression() {
if (type != null && type.equals("log") && expression != null && expression.contains("{")) {
return new Gson().fromJson(expression, LogExpression.class);
}
return new LogExpression();
}

public EmailExpression getEmailExpression() {
if (type != null && type.equals("email") && expression != null && expression.contains("{")) {
return new Gson().fromJson(expression, EmailExpression.class);
}
EmailExpression emailExpression = new EmailExpression();
if (!StringUtils.isEmpty(expression)) {
emailExpression.setEmails(Arrays.asList(expression.split(",", -1)));
}
return emailExpression;
}

public void setExpression(String expression) {
this.expression = expression;
}

public void setExpression(LogExpression expression) {
this.expression = new Gson().toJson(expression);
}

public void setExpression(EmailExpression expression) {
this.expression = new Gson().toJson(expression);
}

public String getSeverityLevelName() {
String name = Monitor.severityLevelNames.get(severityLevel);
if (name == null) {
Expand All @@ -140,4 +174,148 @@ public String getTargetNode() {
public void setTargetNode(String targetNode) {
this.targetNode = targetNode;
}

public class LogExpression {
private String unresolved = "Monitor $(eventType) on $(eventNodeId) reached threshold of"
+ " $(eventThreshold) with a value of $(eventValue)";
private String resolved = "Monitor $(eventType) on $(eventNodeId) is resolved";

public String getUnresolved() {
return unresolved;
}

public void setUnresolved(String unresolved) {
this.unresolved = unresolved;
}

public String getResolved() {
return resolved;
}

public void setResolved(String resolved) {
this.resolved = resolved;
}
}

public class EmailExpression {
private List<String> emails = new ArrayList<String>();
private String subject = "Monitor events for $(eventTypes) from nodes $(eventNodeIds)";
private String bodyBefore = "";
private String bodyAfter = "";
private String unresolved = "Monitor event for $(eventType) reached threshold of"
+ " $(eventThreshold) with a value of $(eventValue)";
private String resolved = "Monitor event for $(eventType) is resolved";
private List<Template> templates = new ArrayList<Template>();

public EmailExpression() {
templates.add(new Template("log", "Details: \n$(eventDetails)"));
templates.add(new Template("batchError", "Details: \n$(eventDetails)"));
templates.add(new Template("offlineNodes", "Details: \n$(eventDetails)"));
templates.add(new Template("batchUnsent", "$(eventValue) batches unsent."));
templates.add(new Template("dataUnrouted", "$(eventValue) unrouted data rows."));
templates.add(new Template("dataGap", "$(eventValue) data gap(s) recorded"));
templates.add(new Template("cpu", "Details: \n$(eventDetails)"));
templates.add(new Template("memory", "Details: \n$(eventDetails)"));
templates.add(new Template("disk", "Disk usage is at $(eventValue)%"));
templates.add(new Template("block", "Details: \n$(eventDetails)"));
templates.add(new Template("default", "Details: \n$(eventDetails)"));
}

public List<String> getEmails() {
return emails;
}

public void setEmails(List<String> emails) {
this.emails = emails;
}

public String getSubject() {
return subject;
}

public void setSubject(String subject) {
this.subject = subject;
}

public String getBodyBefore() {
return bodyBefore;
}

public void setBodyBefore(String bodyBefore) {
this.bodyBefore = bodyBefore;
}

public String getBodyAfter() {
return bodyAfter;
}

public void setBodyAfter(String bodyAfter) {
this.bodyAfter = bodyAfter;
}

public String getUnresolved() {
return unresolved;
}

public void setUnresolved(String unresolved) {
this.unresolved = unresolved;
}

public String getResolved() {
return resolved;
}

public void setResolved(String resolved) {
this.resolved = resolved;
}

public List<Template> getTemplates() {
return templates;
}

public Map<String, String> getTemplateMap() {
Map<String, String> templateMap = new HashMap<String, String>();
for (Template template : templates) {
templateMap.put(template.getName(), template.getTemplate());
}
return templateMap;
}

public void setTemplates(List<Template> templates) {
this.templates = templates;
}

public void setTemplates(Map<String, String> templateMap) {
templates = new ArrayList<Template>();
for (String name : templateMap.keySet()) {
templates.add(new Template(name, templateMap.get(name)));
}
}
}

public class Template {
private String name;
private String template;

public Template(String name, String template) {
this.name = name;
this.template = template;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getTemplate() {
return template;
}

public void setTemplate(String template) {
this.template = template;
}
}
}
Expand Up @@ -21,24 +21,22 @@
package org.jumpmind.symmetric.notification;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.jumpmind.extension.IBuiltInExtensionPoint;
import org.jumpmind.symmetric.ISymmetricEngine;
import org.jumpmind.symmetric.ext.ISymmetricEngineAware;
import org.jumpmind.symmetric.model.IncomingBatch;
import org.jumpmind.symmetric.model.Monitor;
import org.jumpmind.symmetric.model.MonitorEvent;
import org.jumpmind.symmetric.model.Node;
import org.jumpmind.symmetric.model.Notification;
import org.jumpmind.symmetric.model.Notification.EmailExpression;
import org.jumpmind.symmetric.model.OutgoingBatch;
import org.jumpmind.symmetric.monitor.BatchErrorWrapper;
import org.jumpmind.symmetric.util.SymmetricUtils;
import org.jumpmind.util.FormatUtils;
import org.jumpmind.util.LogSummary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -51,84 +49,49 @@ public class NotificationTypeEmail implements INotificationType, ISymmetricEngin
protected ISymmetricEngine engine;

public void notify(Notification notification, List<MonitorEvent> monitorEvents) {
String subject = null;
if (monitorEvents.size() == 1) {
MonitorEvent event = monitorEvents.get(0);
subject = "Monitor event for " + event.getType() + " from node " + event.getNodeId();
} else {
Set<String> nodeIds = new HashSet<String>();
Set<String> types = new HashSet<String>();
for (MonitorEvent event : monitorEvents) {
nodeIds.add(event.getNodeId());
types.add(event.getType());
}
StringBuilder typesString = new StringBuilder();
Iterator<String> iter = types.iterator();
while (iter.hasNext()) {
typesString.append(iter.next());
if (iter.hasNext()) {
typesString.append(", ");
}
}
subject = "Monitor events for " + typesString + " from " + nodeIds.size() + " nodes";
}
Map<String, Node> nodes = engine.getNodeService().findAllNodesAsMap();
Map<String, String> eventListReplacements = SymmetricUtils.getReplacementsForMonitorEventList(engine,
monitorEvents);
EmailExpression expression = notification.getEmailExpression();
String subject = FormatUtils.replaceTokens(expression.getSubject(), eventListReplacements, true);
Map<String, String> templateMap = expression.getTemplateMap();
StringBuilder text = new StringBuilder();
if (!StringUtils.isBlank(expression.getBodyBefore())) {
text.append(FormatUtils.replaceTokens(expression.getBodyBefore(), eventListReplacements, true) + "\n");
}
for (MonitorEvent event : monitorEvents) {
Node node = nodes.get(event.getNodeId());
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String nodeString = node != null ? node.toString() : event.getNodeId();
text.append(dateFormatter.format(event.getEventTime())).append(" [");
text.append(Monitor.getSeverityLevelNames().get(event.getSeverityLevel())).append("] [");
text.append(nodeString).append("] [");
text.append(event.getHostName()).append("] ");
text.append("Monitor event for ").append(event.getType());
if (event.isResolved()) {
text.append(" is resolved\n");
continue;
} else {
text.append(" reached threshold of ").append(event.getThreshold());
text.append(" with a value of ").append(event.getValue()).append("\n");
}
try {
StringBuilder stackTrace = new StringBuilder();
Map<String, String> eventReplacements = SymmetricUtils.getReplacementsForMonitorEvent(engine, event);
if (event.getType().equals("log")) {
stackTrace = getLogDetails(event);
eventReplacements.put("eventDetails", getLogDetails(event));
} else if (event.getType().equals("batchError")) {
stackTrace = getBatchDetails(event);
eventReplacements.put("eventDetails", getBatchDetails(event));
} else if (event.getType().equals("offlineNodes")) {
stackTrace = getOfflineDetails(event);
} else if (event.getType().equals("batchUnsent")) {
stackTrace.append(event.getValue()).append(" batches unsent.").append("\n");
} else if (event.getType().equals("dataUnrouted")) {
stackTrace.append(event.getValue()).append(" unrouted data rows.").append("\n");
} else if (event.getType().equals("dataGap")) {
stackTrace.append(event.getValue()).append(" data gap(s) recorded.").append("\n");
} else if (event.getType().equals("cpu")) {
String details = event.getDetails();
if (details == null || details.length() == 0) {
details = "CPU usage is at " + event.getValue() + "%\n";
}
stackTrace.append(details);
} else if (event.getType().equals("memory")) {
String details = event.getDetails();
if (details == null || details.length() == 0) {
details = "Memory usage is at " + event.getValue() + "%\n";
}
stackTrace.append(details);
} else if (event.getType().equals("disk")) {
stackTrace.append("Disk usage is at ").append(event.getValue()).append("%\n");
eventReplacements.put("eventDetails", getOfflineDetails(event));
}
if (!stackTrace.toString().isEmpty()) {
text.append("\nDetails: ");
text.append(stackTrace.toString());
if (monitorEvents.indexOf(event) > 0) {
text.append("\n");
}
if (event.isResolved()) {
text.append(FormatUtils.replaceTokens(expression.getResolved(), eventReplacements, true));
continue;
} else {
text.append(FormatUtils.replaceTokens(expression.getUnresolved(), eventReplacements, true));
}
String template = templateMap.get(event.getType());
if (template == null) {
template = templateMap.get("default");
}
if (template != null) {
text.append("\n" + FormatUtils.replaceTokens(template, eventReplacements, true));
}
} catch (Exception e) {
log.debug("", e);
}
}
String recipients = notification.getExpression();
if (!StringUtils.isBlank(expression.getBodyAfter())) {
text.append("\n" + FormatUtils.replaceTokens(expression.getBodyAfter(), eventListReplacements, true));
}
String recipients = String.join(",", expression.getEmails());
if (recipients != null) {
log.info("Sending email with subject '" + subject + "' to " + recipients);
engine.getMailService().sendEmail(subject, text.toString(), recipients);
Expand All @@ -137,16 +100,16 @@ public void notify(Notification notification, List<MonitorEvent> monitorEvents)
}
}

protected static StringBuilder getOfflineDetails(MonitorEvent event) throws IOException {
protected static String getOfflineDetails(MonitorEvent event) throws IOException {
StringBuilder stackTrace = new StringBuilder();
stackTrace.append("\n");
for (String node : deserializeOfflineNodes(event)) {
stackTrace.append("Node ").append(node).append(" is offline.").append("\n");
}
return stackTrace;
return stackTrace.toString();
}

protected static StringBuilder getBatchDetails(MonitorEvent event) throws IOException {
protected static String getBatchDetails(MonitorEvent event) throws IOException {
StringBuilder stackTrace = new StringBuilder();
BatchErrorWrapper errors = deserializeBatches(event);
if (errors != null) {
Expand All @@ -161,10 +124,10 @@ protected static StringBuilder getBatchDetails(MonitorEvent event) throws IOExce
stackTrace.append(" failed: ").append(b.getSqlMessage()).append("\n");
}
}
return stackTrace;
return stackTrace.toString();
}

protected static StringBuilder getLogDetails(MonitorEvent event) throws IOException {
protected static String getLogDetails(MonitorEvent event) throws IOException {
StringBuilder stackTrace = new StringBuilder();
int count = 0;
for (LogSummary summary : deserializeLogSummary(event)) {
Expand All @@ -180,7 +143,7 @@ protected static StringBuilder getLogDetails(MonitorEvent event) throws IOExcept
if (count > 0) {
stackTrace.append("\n");
}
return stackTrace;
return stackTrace.toString();
}

protected static List<String> deserializeOfflineNodes(MonitorEvent event) throws IOException {
Expand Down

0 comments on commit a02832c

Please sign in to comment.