Skip to content

Commit

Permalink
Dynamic alert condition config for pluggable alert conditions (#2903)
Browse files Browse the repository at this point in the history
Refs #2867
  • Loading branch information
dennisoelkers authored and joschi committed Oct 7, 2016
1 parent 6a1cbcc commit 5c152cd
Show file tree
Hide file tree
Showing 42 changed files with 753 additions and 349 deletions.
Expand Up @@ -23,6 +23,8 @@
import org.graylog2.plugin.MessageSummary; import org.graylog2.plugin.MessageSummary;
import org.graylog2.plugin.Tools; import org.graylog2.plugin.Tools;
import org.graylog2.plugin.alarms.AlertCondition; import org.graylog2.plugin.alarms.AlertCondition;
import org.graylog2.plugin.configuration.fields.ConfigurationField;
import org.graylog2.plugin.configuration.fields.NumberField;
import org.graylog2.plugin.database.EmbeddedPersistable; import org.graylog2.plugin.database.EmbeddedPersistable;
import org.graylog2.plugin.streams.Stream; import org.graylog2.plugin.streams.Stream;
import org.joda.time.DateTime; import org.joda.time.DateTime;
Expand Down Expand Up @@ -71,7 +73,7 @@ protected AbstractAlertCondition(Stream stream, String id, String type, DateTime
this.type = type; this.type = type;
this.createdAt = createdAt; this.createdAt = createdAt;
this.creatorUserId = creatorUserId; this.creatorUserId = creatorUserId;
this.parameters = parameters; this.parameters = ImmutableMap.copyOf(parameters);


this.grace = getNumber(this.parameters.get("grace")).orElse(0).intValue(); this.grace = getNumber(this.parameters.get("grace")).orElse(0).intValue();
} }
Expand All @@ -85,11 +87,6 @@ public String getType() {
return type; return type;
} }


@Override
public String getTypeString() {
return type.toString();
}

@Override @Override
public String getTitle() { public String getTitle() {
return title; return title;
Expand Down Expand Up @@ -131,7 +128,7 @@ public String toString() {
public Map<String, Object> getPersistedFields() { public Map<String, Object> getPersistedFields() {
return ImmutableMap.<String, Object>builder() return ImmutableMap.<String, Object>builder()
.put("id", id) .put("id", id)
.put("type", type.toString().toLowerCase(Locale.ENGLISH)) .put("type", type)
.put("creator_user_id", creatorUserId) .put("creator_user_id", creatorUserId)
.put("created_at", Tools.getISO8601String(createdAt)) .put("created_at", Tools.getISO8601String(createdAt))
.put("parameters", parameters) .put("parameters", parameters)
Expand Down Expand Up @@ -209,4 +206,11 @@ protected Optional<Number> getNumber(Object o) {
return Optional.empty(); return Optional.empty();
} }
} }

public static List<ConfigurationField> getDefaultConfigurationFields() {
return Lists.newArrayList(
new NumberField("grace", "Grace Period", 0, "Time span in seconds defining how long alerting is paused after alert is triggered", ConfigurationField.Optional.NOT_OPTIONAL),
new NumberField("backlog", "Message Backlog", 0, "The number of messages to be included in alert notification", ConfigurationField.Optional.NOT_OPTIONAL)
);
}
} }
Expand Up @@ -158,7 +158,7 @@ private AlertCondition createAlertCondition(String type,
@Override @Override
public AlertCondition fromRequest(CreateConditionRequest ccr, Stream stream, String userId) { public AlertCondition fromRequest(CreateConditionRequest ccr, Stream stream, String userId) {
final String type = ccr.type(); final String type = ccr.type();
checkArgument(type != null, "Milling alert condition type"); checkArgument(type != null, "Missing alert condition type");


return createAlertCondition(type, stream, null, Tools.nowUTC(), userId, ccr.parameters(), ccr.title()); return createAlertCondition(type, stream, null, Tools.nowUTC(), userId, ccr.parameters(), ccr.title());
} }
Expand Down Expand Up @@ -217,7 +217,7 @@ public AlertCondition.CheckResult triggered(AlertCondition alertCondition) {
public Map<String, Object> asMap(final AlertCondition alertCondition) { public Map<String, Object> asMap(final AlertCondition alertCondition) {
ImmutableMap.Builder<String, Object> builder = ImmutableMap.<String, Object>builder() ImmutableMap.Builder<String, Object> builder = ImmutableMap.<String, Object>builder()
.put("id", alertCondition.getId()) .put("id", alertCondition.getId())
.put("type", alertCondition.getTypeString().toLowerCase(Locale.ENGLISH)) .put("type", alertCondition.getType())
.put("creator_user_id", alertCondition.getCreatorUserId()) .put("creator_user_id", alertCondition.getCreatorUserId())
.put("created_at", Tools.getISO8601String(alertCondition.getCreatedAt())) .put("created_at", Tools.getISO8601String(alertCondition.getCreatedAt()))
.put("parameters", alertCondition.getParameters()) .put("parameters", alertCondition.getParameters())
Expand Down
Expand Up @@ -27,12 +27,15 @@
import org.graylog2.indexer.results.SearchResult; import org.graylog2.indexer.results.SearchResult;
import org.graylog2.indexer.searches.Searches; import org.graylog2.indexer.searches.Searches;
import org.graylog2.indexer.searches.Sorting; import org.graylog2.indexer.searches.Sorting;
import org.graylog2.plugin.alarms.AlertCondition;
import org.graylog2.plugin.indexer.searches.timeranges.InvalidRangeParametersException;
import org.graylog2.plugin.indexer.searches.timeranges.RelativeRange;
import org.graylog2.plugin.Message; import org.graylog2.plugin.Message;
import org.graylog2.plugin.MessageSummary; import org.graylog2.plugin.MessageSummary;
import org.graylog2.plugin.Tools; import org.graylog2.plugin.Tools;
import org.graylog2.plugin.alarms.AlertCondition;
import org.graylog2.plugin.configuration.ConfigurationRequest;
import org.graylog2.plugin.configuration.fields.ConfigurationField;
import org.graylog2.plugin.configuration.fields.TextField;
import org.graylog2.plugin.indexer.searches.timeranges.InvalidRangeParametersException;
import org.graylog2.plugin.indexer.searches.timeranges.RelativeRange;
import org.graylog2.plugin.streams.Stream; import org.graylog2.plugin.streams.Stream;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.slf4j.Logger; import org.slf4j.Logger;
Expand Down Expand Up @@ -61,6 +64,33 @@ FieldContentValueAlertCondition create(Stream stream,
@Assisted("userid") String creatorUserId, @Assisted("userid") String creatorUserId,
Map<String, Object> parameters, Map<String, Object> parameters,
@Assisted("title") @Nullable String title); @Assisted("title") @Nullable String title);

Config config();
Descriptor descriptor();
}

public static class Config implements AlertCondition.Config {
public Config() {
}

@Override
public ConfigurationRequest getRequestedConfiguration() {
return ConfigurationRequest.createWithFields(
new TextField("field", "Field", "", "Field name that should be checked", ConfigurationField.Optional.NOT_OPTIONAL),
new TextField("value", "Value", "", "Value that the field should be checked against", ConfigurationField.Optional.NOT_OPTIONAL)
).addFields(AbstractAlertCondition.getDefaultConfigurationFields());
}
}

public static class Descriptor extends AlertCondition.Descriptor {
public Descriptor() {
super(
"Field Content Value Alert Condition",
"https://www.graylog.org/",
"This condition is triggered when the aggregated value of a field is higher/lower than a defined "
+ "threshold for a given time range."
);
}
} }


@AssistedInject @AssistedInject
Expand Down
Expand Up @@ -24,12 +24,17 @@
import org.graylog2.indexer.results.FieldStatsResult; import org.graylog2.indexer.results.FieldStatsResult;
import org.graylog2.indexer.results.ResultMessage; import org.graylog2.indexer.results.ResultMessage;
import org.graylog2.indexer.searches.Searches; import org.graylog2.indexer.searches.Searches;
import org.graylog2.plugin.alarms.AlertCondition;
import org.graylog2.plugin.indexer.searches.timeranges.InvalidRangeParametersException;
import org.graylog2.plugin.indexer.searches.timeranges.RelativeRange;
import org.graylog2.plugin.Message; import org.graylog2.plugin.Message;
import org.graylog2.plugin.MessageSummary; import org.graylog2.plugin.MessageSummary;
import org.graylog2.plugin.Tools; import org.graylog2.plugin.Tools;
import org.graylog2.plugin.alarms.AlertCondition;
import org.graylog2.plugin.configuration.ConfigurationRequest;
import org.graylog2.plugin.configuration.fields.ConfigurationField;
import org.graylog2.plugin.configuration.fields.DropdownField;
import org.graylog2.plugin.configuration.fields.NumberField;
import org.graylog2.plugin.configuration.fields.TextField;
import org.graylog2.plugin.indexer.searches.timeranges.InvalidRangeParametersException;
import org.graylog2.plugin.indexer.searches.timeranges.RelativeRange;
import org.graylog2.plugin.streams.Stream; import org.graylog2.plugin.streams.Stream;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.slf4j.Logger; import org.slf4j.Logger;
Expand All @@ -38,32 +43,85 @@
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;


import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.base.Strings.isNullOrEmpty;


public class FieldValueAlertCondition extends AbstractAlertCondition { public class FieldValueAlertCondition extends AbstractAlertCondition {
private static final Logger LOG = LoggerFactory.getLogger(FieldValueAlertCondition.class); private static final Logger LOG = LoggerFactory.getLogger(FieldValueAlertCondition.class);


public enum CheckType { enum CheckType {
MEAN, MIN, MAX, SUM, STDDEV MEAN("mean value"), MIN("min value"), MAX("max value"), SUM("sum"), STDDEV("standard deviation");

private final String description;

CheckType(String description) {
this.description = description;
}

public String getDescription() {
return description;
}
} }


public enum ThresholdType { enum ThresholdType {
LOWER, HIGHER LOWER, HIGHER
} }


public interface Factory extends AlertCondition.Factory { public interface Factory extends AlertCondition.Factory {
@Override
FieldValueAlertCondition create(Stream stream, FieldValueAlertCondition create(Stream stream,
@Assisted("id") String id, @Assisted("id") String id,
DateTime createdAt, DateTime createdAt,
@Assisted("userid") String creatorUserId, @Assisted("userid") String creatorUserId,
Map<String, Object> parameters, Map<String, Object> parameters,
@Assisted("title") @Nullable String title); @Assisted("title") @Nullable String title);
@Override
Config config();
@Override
Descriptor descriptor();
}

public static class Config implements AlertCondition.Config {
public Config() {
}

@Override
public ConfigurationRequest getRequestedConfiguration() {
return ConfigurationRequest.createWithFields(
new TextField("field", "Field", "", "Field name that should be checked", ConfigurationField.Optional.NOT_OPTIONAL),
new NumberField("time", "Time Range", 0, "Time span in seconds to check", ConfigurationField.Optional.NOT_OPTIONAL),
new NumberField("threshold", "Threshold", 0.0, "Value which triggers an alert if crossed", ConfigurationField.Optional.NOT_OPTIONAL),
new DropdownField(
"threshold_type",
"Threshold Type",
ThresholdType.HIGHER.toString(),
DropdownField.ValueTemplates.valueMapFromEnum(ThresholdType.class, thresholdType -> thresholdType.name().toLowerCase(Locale.ENGLISH)),
ConfigurationField.Optional.NOT_OPTIONAL),
new DropdownField(
"type",
"Check Type",
CheckType.MAX.toString(),
Arrays.stream(CheckType.values()).collect(Collectors.toMap(Enum::toString, CheckType::getDescription)),
ConfigurationField.Optional.NOT_OPTIONAL)
).addFields(AbstractAlertCondition.getDefaultConfigurationFields());
}
}

public static class Descriptor extends AlertCondition.Descriptor {
public Descriptor() {
super(
"Field Value Alert Condition",
"https://www.graylog.org/",
"This condition is triggered when the content of messages is equal to a defined value."
);
}
} }


private final int time; private final int time;
Expand Down
Expand Up @@ -26,28 +26,46 @@
import org.graylog2.indexer.results.SearchResult; import org.graylog2.indexer.results.SearchResult;
import org.graylog2.indexer.searches.Searches; import org.graylog2.indexer.searches.Searches;
import org.graylog2.indexer.searches.Sorting; import org.graylog2.indexer.searches.Sorting;
import org.graylog2.plugin.Message;
import org.graylog2.plugin.MessageSummary;
import org.graylog2.plugin.Tools;
import org.graylog2.plugin.alarms.AlertCondition; import org.graylog2.plugin.alarms.AlertCondition;
import org.graylog2.plugin.configuration.ConfigurationRequest;
import org.graylog2.plugin.configuration.fields.ConfigurationField;
import org.graylog2.plugin.configuration.fields.DropdownField;
import org.graylog2.plugin.configuration.fields.NumberField;
import org.graylog2.plugin.indexer.searches.timeranges.AbsoluteRange; import org.graylog2.plugin.indexer.searches.timeranges.AbsoluteRange;
import org.graylog2.plugin.indexer.searches.timeranges.InvalidRangeParametersException; import org.graylog2.plugin.indexer.searches.timeranges.InvalidRangeParametersException;
import org.graylog2.plugin.indexer.searches.timeranges.RelativeRange; import org.graylog2.plugin.indexer.searches.timeranges.RelativeRange;
import org.graylog2.plugin.Message;
import org.graylog2.plugin.MessageSummary;
import org.graylog2.plugin.Tools;
import org.graylog2.plugin.streams.Stream; import org.graylog2.plugin.streams.Stream;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;


import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;


public class MessageCountAlertCondition extends AbstractAlertCondition { public class MessageCountAlertCondition extends AbstractAlertCondition {
private static final Logger LOG = LoggerFactory.getLogger(MessageCountAlertCondition.class); private static final Logger LOG = LoggerFactory.getLogger(MessageCountAlertCondition.class);


public enum ThresholdType { enum ThresholdType {
MORE, LESS
MORE("more than"),
LESS("less than");

private final String description;

ThresholdType(String description) {
this.description = description;
}

public String getDescription() {
return description;
}
} }


public interface Factory extends AlertCondition.Factory { public interface Factory extends AlertCondition.Factory {
Expand All @@ -57,6 +75,37 @@ MessageCountAlertCondition create(Stream stream,
@Assisted("userid") String creatorUserId, @Assisted("userid") String creatorUserId,
Map<String, Object> parameters, Map<String, Object> parameters,
@Assisted("title") @Nullable String title); @Assisted("title") @Nullable String title);
Config config();
Descriptor descriptor();
}

public static class Config implements AlertCondition.Config {
public Config() {
}

@Override
public ConfigurationRequest getRequestedConfiguration() {
return ConfigurationRequest.createWithFields(
new NumberField("time", "Time Range", 0, "Time span in seconds to check", ConfigurationField.Optional.NOT_OPTIONAL),
new NumberField("threshold", "Threshold", 0.0, "Value which triggers an alert if crossed", ConfigurationField.Optional.NOT_OPTIONAL),
new DropdownField(
"threshold_type",
"Threshold Type",
MessageCountAlertCondition.ThresholdType.MORE.toString(),
Arrays.stream(MessageCountAlertCondition.ThresholdType.values()).collect(Collectors.toMap(Enum::toString, ThresholdType::getDescription)),
ConfigurationField.Optional.NOT_OPTIONAL)
).addFields(AbstractAlertCondition.getDefaultConfigurationFields());
}
}

public static class Descriptor extends AlertCondition.Descriptor {
public Descriptor() {
super(
"Message Count Alert Condition",
"https://www.graylog.org/",
"This condition is triggered when the number of messages in a defined time interval is higher or lower a defined threshold."
);
}
} }


private final int time; private final int time;
Expand Down
@@ -0,0 +1,30 @@
/**
* This file is part of Graylog.
*
* Graylog is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Graylog is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Graylog. If not, see <http://www.gnu.org/licenses/>.
*/
package org.graylog2.plugin;

public class DescriptorWithHumanName extends AbstractDescriptor {
private final String humanName;

public DescriptorWithHumanName(String name, boolean exclusive, String linkToDocs, String humanName) {
super(name, exclusive, linkToDocs);
this.humanName = humanName;
}

public String getHumanName() {
return humanName;
}
}
Expand Up @@ -17,7 +17,9 @@
package org.graylog2.plugin.alarms; package org.graylog2.plugin.alarms;


import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import org.graylog2.plugin.DescriptorWithHumanName;
import org.graylog2.plugin.MessageSummary; import org.graylog2.plugin.MessageSummary;
import org.graylog2.plugin.configuration.ConfigurationRequest;
import org.graylog2.plugin.streams.Stream; import org.graylog2.plugin.streams.Stream;
import org.joda.time.DateTime; import org.joda.time.DateTime;


Expand Down Expand Up @@ -45,8 +47,7 @@ public interface AlertCondition {
@JsonIgnore @JsonIgnore
int getGrace(); int getGrace();


@JsonIgnore String getType();
String getTypeString();


String getTitle(); String getTitle();


Expand All @@ -73,5 +74,17 @@ AlertCondition create(Stream stream,
String creatorUserId, String creatorUserId,
Map<String, Object> parameters, Map<String, Object> parameters,
@Nullable String title); @Nullable String title);
Config config();
Descriptor descriptor();
}

abstract class Descriptor extends DescriptorWithHumanName {
public Descriptor(String name, String linkToDocs, String humanName) {
super(name, false, linkToDocs, humanName);
}
}

interface Config {
ConfigurationRequest getRequestedConfiguration();
} }
} }

0 comments on commit 5c152cd

Please sign in to comment.