Skip to content

Commit 2eca828

Browse files
StefanHufschmidtjschaefer-pott
authored andcommitted
Added support for custom labels and annotations
Signed-off-by: Decker, Stefan <[email protected]>
1 parent 7e5bacb commit 2eca828

File tree

8 files changed

+375
-13
lines changed

8 files changed

+375
-13
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ The values `startsAt`, `endsAt` and `generatorURL` will be transmitted to the Al
2828
`startsAt` will be set to the point of time when the condition triggered the alert.
2929
`endsAt` will be set to the point of time when the condition triggered the alert plus the set grace time which is configured for the alert.
3030

31+
Additionally you can configure your own custom annotations and labels which should be submitted to the AlertManager (see screenshot below).
32+
3133
## How to deploy on Graylog
3234
You can easily build the plugin by executing `./gradlew build`.
3335
Afterwards there should be a `.jar` file inside the `build/libs/` directory.
@@ -37,9 +39,6 @@ Follow the instructions mentioned [here](http://docs.graylog.org/en/2.4/pages/pl
3739
![Configuration of Callback](images/New_AlertManager_Callback_Window.png)
3840

3941
## Planned Features
40-
* Add possibility to define custom labels in UI when configuring the callback
41-
* Add possibility to define custom annotations in UI when configuring the callback
42-
4342
You would like to contribute anything? - Take a look at [CONTRIBUTING.md](CONTRIBUTING.md).
4443

4544
## Known Issues
27.1 KB
Loading

src/main/java/de/gdata/mobilelab/alertmanagercallback/AlertManagerAlarmCallback.java

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,25 @@
1212
import org.graylog2.plugin.configuration.fields.TextField;
1313
import org.graylog2.plugin.streams.Stream;
1414

15+
import java.io.IOException;
1516
import java.net.URI;
1617
import java.net.URISyntaxException;
1718
import java.util.Map;
1819

1920
public class AlertManagerAlarmCallback implements AlarmCallback {
2021

22+
static final String CONFIGURATION_KEY_API_URL = "alertmanager_api_url";
23+
static final String CONFIGURATION_KEY_ALERT_NAME = "alertmanager_alert_name";
24+
static final String CONFIGURATION_KEY_CUSTOM_LABELS = "alertmanager_custom_labels";
25+
static final String CONFIGURATION_KEY_CUSTOM_ANNOTATIONS = "alertmanager_custom_annotations";
26+
2127
private Configuration configuration;
2228
private AlertManagerPostRequestSender alertManagerPostRequestSender;
2329

2430
@Override
2531
public void initialize(Configuration config) throws AlarmCallbackConfigurationException {
2632
configuration = config;
27-
alertManagerPostRequestSender = new AlertManagerPostRequestSender(config.getString("alertmanager_api_url"));
33+
alertManagerPostRequestSender = new AlertManagerPostRequestSender(config.getString(CONFIGURATION_KEY_API_URL));
2834
}
2935

3036
@Override
@@ -44,7 +50,7 @@ public ConfigurationRequest getRequestedConfiguration() {
4450

4551
// API URL
4652
ConfigurationField alertmanagerApiUrl = new TextField(
47-
"alertmanager_api_url",
53+
CONFIGURATION_KEY_API_URL,
4854
"AlertManager API URL",
4955
"http://localhost:9093/api/v1/alerts",
5056
"This callback sends a POST-Request to an AlertManager API. It converts the information into a format which is readable by the AlertManager.",
@@ -54,14 +60,34 @@ public ConfigurationRequest getRequestedConfiguration() {
5460

5561
// Alert Name
5662
ConfigurationField alertName = new TextField(
57-
"alertmanager_alert_name",
63+
CONFIGURATION_KEY_ALERT_NAME,
5864
"AlertManager Alert Name",
5965
"TestAlert",
6066
"The name for the specific AlertManager alert (will be transmitted as 'alertname'-label).",
6167
Optional.NOT_OPTIONAL
6268
);
6369
configurationRequest.addField(alertName);
6470

71+
// Custom labels
72+
ConfigurationField customLabels = new TextField(
73+
CONFIGURATION_KEY_CUSTOM_LABELS,
74+
"Custom AlertManager labels",
75+
"",
76+
"The custom AlertManager label key-value-pairs separated by '" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "' to set for each alert. Please use the following notation: 'label1=value1" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "label2=value2'",
77+
Optional.OPTIONAL
78+
);
79+
configurationRequest.addField(customLabels);
80+
81+
// Custom annotations
82+
ConfigurationField customAnnotations = new TextField(
83+
CONFIGURATION_KEY_CUSTOM_ANNOTATIONS,
84+
"Custom AlertManager annotations",
85+
"",
86+
"The custom AlertManager annotation key-value-pairs separated by '" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "' to set for each alert. Please use the following notation: 'annotation1=value1" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "annotation2=value2'",
87+
Optional.OPTIONAL
88+
);
89+
configurationRequest.addField(customAnnotations);
90+
6591
return configurationRequest;
6692
}
6793

@@ -77,14 +103,34 @@ public Map<String, Object> getAttributes() {
77103

78104
@Override
79105
public void checkConfiguration() throws ConfigurationException {
80-
String apiUrl = configuration.getString("alertmanager_api_url");
106+
final String apiUrl = configuration.getString(CONFIGURATION_KEY_API_URL);
81107
if (apiUrl == null) {
82108
throw new ConfigurationException("AlertManager API URL has to be set.");
83109
}
110+
84111
try {
85112
new URI(apiUrl);
86113
} catch (URISyntaxException e) {
87114
throw new ConfigurationException("The URL: '" + apiUrl + "' is not a valid URL. " + e.getMessage());
88115
}
116+
117+
118+
CustomPropertiesTextFieldParser customPropertiesTextFieldParser = new CustomPropertiesTextFieldParser();
119+
120+
final String customLabels = configuration.getString(CONFIGURATION_KEY_CUSTOM_LABELS);
121+
try {
122+
customPropertiesTextFieldParser.extractKeyValuePairsFromCustomField(customLabels);
123+
} catch (IOException e) {
124+
// Not a valid configuration for custom labels
125+
throw new ConfigurationException("The format for given custom labels is invalid. " + e.getMessage());
126+
}
127+
128+
final String customAnnotations = configuration.getString(CONFIGURATION_KEY_CUSTOM_ANNOTATIONS);
129+
try {
130+
customPropertiesTextFieldParser.extractKeyValuePairsFromCustomField(customAnnotations);
131+
} catch (IOException e) {
132+
// Not a valid configuration for custom labels
133+
throw new ConfigurationException("The format for given custom annotations is invalid. " + e.getMessage());
134+
}
89135
}
90136
}

src/main/java/de/gdata/mobilelab/alertmanagercallback/AlertManagerPayloadBuilder.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,22 @@
55
import org.graylog2.plugin.streams.Stream;
66
import org.joda.time.DateTime;
77

8+
import java.io.IOException;
89
import java.util.HashMap;
910
import java.util.Map;
1011

1112
class AlertManagerPayloadBuilder {
1213

13-
private static final String STREAM_TITLE_KEY = "stream_title";
14-
private static final String ALERTMANAGER_ALERT_NAME_KEY = "alertmanager_alert_name";
1514
private static final String ALERTNAME_KEY = "alertname";
15+
1616
private Stream stream;
1717
private AlertCondition.CheckResult checkResult;
1818
private Configuration configuration;
19+
private CustomPropertiesTextFieldParser customPropertiesTextFieldParser;
1920

2021
private AlertManagerPayloadBuilder() {
2122
// Private constructor to hide the implicit one
23+
customPropertiesTextFieldParser = new CustomPropertiesTextFieldParser();
2224
}
2325

2426
static AlertManagerPayloadBuilder newInstance() {
@@ -56,20 +58,28 @@ AlertManagerPayload build() {
5658

5759
private Map<String, Object> extractLabels() {
5860
Map<String, Object> labels = new HashMap<>();
59-
if(configuration != null && configuration.getString(ALERTMANAGER_ALERT_NAME_KEY) != null) {
60-
labels.put(ALERTNAME_KEY, configuration.getString(ALERTMANAGER_ALERT_NAME_KEY));
61+
if(configuration != null && configuration.getString(AlertManagerAlarmCallback.CONFIGURATION_KEY_ALERT_NAME) != null) {
62+
labels.put(ALERTNAME_KEY, configuration.getString(AlertManagerAlarmCallback.CONFIGURATION_KEY_ALERT_NAME));
6163
} else {
6264
labels.put(ALERTNAME_KEY, "Please add a valid configuration object to AlertManager plugin.");
6365
}
6466

67+
// custom labels
68+
final String customLabelString = configuration != null ? configuration.getString(AlertManagerAlarmCallback.CONFIGURATION_KEY_CUSTOM_LABELS) : null;
69+
try {
70+
labels.putAll(customPropertiesTextFieldParser.extractKeyValuePairsFromCustomField(customLabelString));
71+
} catch (IOException e) {
72+
// damaged configuration, so we'll not put any additional label into the map
73+
}
74+
6575
return labels;
6676
}
6777

6878
private Map<String, Object> extractAnnotations() {
6979
Map<String, Object> annotations = new HashMap<>();
7080

7181
if(stream != null && stream.getTitle() != null) {
72-
annotations.put(STREAM_TITLE_KEY, stream.getTitle());
82+
annotations.put("stream_title", stream.getTitle());
7383
}
7484

7585
if(checkResult != null) {
@@ -80,6 +90,14 @@ private Map<String, Object> extractAnnotations() {
8090
}
8191
}
8292

93+
// custom annotations
94+
final String customAnnotationString = configuration != null ? configuration.getString(AlertManagerAlarmCallback.CONFIGURATION_KEY_CUSTOM_ANNOTATIONS) : null;
95+
try {
96+
annotations.putAll(customPropertiesTextFieldParser.extractKeyValuePairsFromCustomField(customAnnotationString));
97+
} catch (IOException e) {
98+
// damaged configuration, so we'll not put any additional annotation into the map
99+
}
100+
83101
return annotations;
84102
}
85103

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package de.gdata.mobilelab.alertmanagercallback;
2+
3+
import java.io.ByteArrayInputStream;
4+
import java.io.IOException;
5+
import java.io.InputStream;
6+
import java.nio.charset.StandardCharsets;
7+
import java.util.HashMap;
8+
import java.util.Map;
9+
import java.util.Properties;
10+
11+
/**
12+
* This parser can parse key-value-pairs from a text field string.
13+
* It uses {@link #KEY_VALUE_PAIR_SEPARATOR} for separating each key-value-pair.
14+
* The keys and related values will be parsed using {@link Properties#load(InputStream)}.
15+
*/
16+
class CustomPropertiesTextFieldParser {
17+
18+
/** The separator used in text field to split between key-value-pairs. */
19+
static final String KEY_VALUE_PAIR_SEPARATOR = ";";
20+
21+
/** The separator used by {@link Properties#load(InputStream)} for key-value-pair separation. */
22+
private static final String PROPERTIES_KEY_VALUE_PAIR_SEPARATOR = "\n";
23+
24+
/**
25+
* Parses the text field value with custom key-value-pairs into a map.<br>
26+
* It uses the {@link #KEY_VALUE_PAIR_SEPARATOR} for the line separation. This separation is required
27+
* for loading the key-value-pairs using {@link Properties#load(InputStream)}.
28+
*
29+
* @param textFieldValue the text field value
30+
* @return the map with key-value-pairs from text field
31+
* @throws IOException if parsing the key-value-pairs fails
32+
*/
33+
Map<? extends String, ?> extractKeyValuePairsFromCustomField(String textFieldValue) throws IOException {
34+
Map<String, Object> extractedPairs = new HashMap<>();
35+
36+
if (textFieldValue != null && !"".equals(textFieldValue)) {
37+
final String preparedTextFieldValue = textFieldValue.replaceAll(KEY_VALUE_PAIR_SEPARATOR, PROPERTIES_KEY_VALUE_PAIR_SEPARATOR);
38+
Properties properties = new Properties();
39+
InputStream stringInputStream = new ByteArrayInputStream(preparedTextFieldValue.getBytes(StandardCharsets.UTF_8));
40+
properties.load(stringInputStream);
41+
properties.forEach((key, value) -> extractedPairs.put((String) key, value));
42+
}
43+
44+
return extractedPairs;
45+
}
46+
47+
}

src/test/java/de/gdata/mobilelab/alertmanagercallback/AlertManagerAlarmCallbackTest.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public void getRequestedConfiguration() {
5151
ConfigurationRequest configurationRequest = alertManagerAlarmCallback.getRequestedConfiguration();
5252

5353
// then: text fields have been set
54-
assertEquals(2, configurationRequest.getFields().size());
54+
assertEquals(4, configurationRequest.getFields().size());
5555

5656
ConfigurationField field = configurationRequest.getField("alertmanager_api_url");
5757
assertNotNull(field);
@@ -69,6 +69,22 @@ public void getRequestedConfiguration() {
6969
assertEquals("TestAlert", field.getDefaultValue());
7070
assertEquals("The name for the specific AlertManager alert (will be transmitted as 'alertname'-label).", field.getDescription());
7171
assertEquals(ConfigurationField.Optional.NOT_OPTIONAL, field.isOptional());
72+
73+
field = configurationRequest.getField("alertmanager_custom_labels");
74+
assertNotNull(field);
75+
assertEquals(TextField.FIELD_TYPE, field.getFieldType());
76+
assertEquals("Custom AlertManager labels", field.getHumanName());
77+
assertEquals("", field.getDefaultValue());
78+
assertEquals("The custom AlertManager label key-value-pairs separated by '" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "' to set for each alert. Please use the following notation: 'label1=value1" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "label2=value2'", field.getDescription());
79+
assertEquals(ConfigurationField.Optional.OPTIONAL, field.isOptional());
80+
81+
field = configurationRequest.getField("alertmanager_custom_annotations");
82+
assertNotNull(field);
83+
assertEquals(TextField.FIELD_TYPE, field.getFieldType());
84+
assertEquals("Custom AlertManager annotations", field.getHumanName());
85+
assertEquals("", field.getDefaultValue());
86+
assertEquals("The custom AlertManager annotation key-value-pairs separated by '" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "' to set for each alert. Please use the following notation: 'annotation1=value1" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "annotation2=value2'", field.getDescription());
87+
assertEquals(ConfigurationField.Optional.OPTIONAL, field.isOptional());
7288
}
7389

7490
@Test

0 commit comments

Comments
 (0)