Skip to content
This repository was archived by the owner on Jun 29, 2018. It is now read-only.

Commit 215f2a1

Browse files
committed
Add pagerduty notification
1 parent 06e1423 commit 215f2a1

File tree

12 files changed

+395
-104
lines changed

12 files changed

+395
-104
lines changed

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/config/AdminServerImportSelector.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ public String[] selectImports(AnnotationMetadata importingClassMetadata) {
3232
HazelcastStoreConfiguration.class.getCanonicalName(),
3333
AdminServerWebConfiguration.class.getCanonicalName(),
3434
DiscoveryClientConfiguration.class.getCanonicalName(),
35-
RevereseZuulProxyConfiguration.class.getCanonicalName() };
35+
RevereseZuulProxyConfiguration.class.getCanonicalName(),
36+
PagerdutyNotifierConfiguration.class.getCanonicalName() };
3637
}
3738

3839
}

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/config/MailNotifierConfiguration.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,4 @@ public class MailNotifierConfiguration {
4343
public MailNotifier mailNotifier() {
4444
return new MailNotifier(mailSender);
4545
}
46-
4746
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package de.codecentric.boot.admin.config;
17+
18+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
19+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
20+
import org.springframework.boot.context.properties.ConfigurationProperties;
21+
import org.springframework.context.annotation.Bean;
22+
import org.springframework.context.annotation.Configuration;
23+
24+
import de.codecentric.boot.admin.notify.PagerdutyNotifier;
25+
26+
@Configuration
27+
@ConditionalOnProperty("spring.boot.admin.notify.pagerduty.service-key")
28+
public class PagerdutyNotifierConfiguration {
29+
30+
@Bean
31+
@ConditionalOnMissingBean
32+
@ConditionalOnProperty(prefix = "spring.boot.admin.notify.pagerduty", name = "enabled", matchIfMissing = true)
33+
@ConfigurationProperties("spring.boot.admin.notify.pagerduty")
34+
public PagerdutyNotifier pagerdutyNotifier() {
35+
return new PagerdutyNotifier();
36+
}
37+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package de.codecentric.boot.admin.notify;
2+
3+
import java.util.Arrays;
4+
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
import org.springframework.context.event.EventListener;
8+
9+
import de.codecentric.boot.admin.event.ClientApplicationStatusChangedEvent;
10+
11+
public abstract class AbstractNotifier {
12+
13+
/**
14+
* List of changes to ignore. Must be in Format OLD:NEW, for any status use * as wildcard, e.g.
15+
* *:UP or OFFLINE:*
16+
*/
17+
protected String[] ignoreChanges = { "UNKNOWN:UP" };
18+
19+
/**
20+
* Enables the mail notification.
21+
*/
22+
private boolean enabled = true;
23+
24+
@EventListener
25+
public void onClientApplicationStatusChanged(ClientApplicationStatusChangedEvent event) {
26+
if (enabled && shouldNotify(event.getFrom().getStatus(), event.getTo().getStatus())) {
27+
try {
28+
notify(event);
29+
} catch (Exception ex) {
30+
getLogger().error("Couldn't notify for status change {} ", event, ex);
31+
}
32+
}
33+
}
34+
35+
protected boolean shouldNotify(String from, String to) {
36+
return Arrays.binarySearch(ignoreChanges, (from + ":" + to)) < 0
37+
&& Arrays.binarySearch(ignoreChanges, ("*:" + to)) < 0
38+
&& Arrays.binarySearch(ignoreChanges, (from + ":*")) < 0;
39+
}
40+
41+
protected abstract void notify(ClientApplicationStatusChangedEvent event) throws Exception;
42+
43+
private Logger getLogger() {
44+
return LoggerFactory.getLogger(this.getClass());
45+
}
46+
47+
public void setIgnoreChanges(String[] ignoreChanges) {
48+
String[] copy = Arrays.copyOf(ignoreChanges, ignoreChanges.length);
49+
Arrays.sort(copy);
50+
this.ignoreChanges = copy;
51+
}
52+
53+
public void setEnabled(boolean enabled) {
54+
this.enabled = enabled;
55+
}
56+
}

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/notify/MailNotifier.java

Lines changed: 5 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@
1717

1818
import java.util.Arrays;
1919

20-
import javax.mail.MessagingException;
21-
22-
import org.slf4j.Logger;
23-
import org.slf4j.LoggerFactory;
24-
import org.springframework.context.event.EventListener;
2520
import org.springframework.expression.EvaluationContext;
2621
import org.springframework.expression.Expression;
2722
import org.springframework.expression.ParserContext;
@@ -33,14 +28,11 @@
3328

3429
import de.codecentric.boot.admin.event.ClientApplicationStatusChangedEvent;
3530

36-
public class MailNotifier {
37-
38-
private static final Logger LOGGER = LoggerFactory.getLogger(MailNotifier.class);
39-
private final String DEFAULT_SUBJECT = "#{application.name} (#{application.id}) is #{to.status}";
40-
private final String DEFAULT_TEXT = "#{application.name} (#{application.id})\nstatus changed from #{from.status} to #{to.status}\n\n#{application.healthUrl}";
31+
public class MailNotifier extends AbstractNotifier {
32+
private final static String DEFAULT_SUBJECT = "#{application.name} (#{application.id}) is #{to.status}";
33+
private final static String DEFAULT_TEXT = "#{application.name} (#{application.id})\nstatus changed from #{from.status} to #{to.status}\n\n#{application.healthUrl}";
4134

4235
private final SpelExpressionParser parser = new SpelExpressionParser();
43-
4436
private MailSender sender;
4537

4638
/**
@@ -68,35 +60,14 @@ public class MailNotifier {
6860
*/
6961
private Expression subject;
7062

71-
/**
72-
* List of changes to ignore. Must be in Format OLD:NEW, for any status use * as wildcard, e.g.
73-
* *:UP or OFFLINE:*
74-
*/
75-
private String[] ignoreChanges = { "UNKNOWN:UP" };
76-
77-
/**
78-
* Enables the mail notification.
79-
*/
80-
private boolean enabled = true;
81-
8263
public MailNotifier(MailSender sender) {
8364
this.sender = sender;
8465
this.subject = parser.parseExpression(DEFAULT_SUBJECT, ParserContext.TEMPLATE_EXPRESSION);
8566
this.text = parser.parseExpression(DEFAULT_TEXT, ParserContext.TEMPLATE_EXPRESSION);
8667
}
8768

88-
@EventListener
89-
public void onClientApplicationStatusChanged(ClientApplicationStatusChangedEvent event) {
90-
if (enabled && shouldSendMail(event.getFrom().getStatus(), event.getTo().getStatus())) {
91-
try {
92-
sendMail(event);
93-
} catch (Exception ex) {
94-
LOGGER.error("Couldn't send mail for Statuschange {} ", event, ex);
95-
}
96-
}
97-
}
98-
99-
private void sendMail(ClientApplicationStatusChangedEvent event) throws MessagingException {
69+
@Override
70+
protected void notify(ClientApplicationStatusChangedEvent event) {
10071
EvaluationContext context = new StandardEvaluationContext(event);
10172

10273
SimpleMailMessage message = new SimpleMailMessage();
@@ -109,12 +80,6 @@ private void sendMail(ClientApplicationStatusChangedEvent event) throws Messagin
10980
sender.send(message);
11081
}
11182

112-
private boolean shouldSendMail(String from, String to) {
113-
return Arrays.binarySearch(ignoreChanges, (from + ":" + to)) < 0
114-
&& Arrays.binarySearch(ignoreChanges, ("*:" + to)) < 0
115-
&& Arrays.binarySearch(ignoreChanges, (from + ":*")) < 0;
116-
}
117-
11883
public void setSender(JavaMailSender sender) {
11984
this.sender = sender;
12085
}
@@ -139,14 +104,4 @@ public void setText(String text) {
139104
this.text = parser.parseExpression(text, ParserContext.TEMPLATE_EXPRESSION);
140105
}
141106

142-
public void setIgnoreChanges(String[] ignoreChanges) {
143-
String[] copy = Arrays.copyOf(ignoreChanges, ignoreChanges.length);
144-
Arrays.sort(copy);
145-
this.ignoreChanges = copy;
146-
}
147-
148-
public void setEnabled(boolean enabled) {
149-
this.enabled = enabled;
150-
}
151-
152107
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright 2013-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package de.codecentric.boot.admin.notify;
17+
18+
import java.net.URI;
19+
import java.util.Arrays;
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
23+
import org.springframework.expression.Expression;
24+
import org.springframework.expression.ParserContext;
25+
import org.springframework.expression.spel.standard.SpelExpressionParser;
26+
import org.springframework.web.client.RestTemplate;
27+
28+
import de.codecentric.boot.admin.event.ClientApplicationStatusChangedEvent;
29+
30+
public class PagerdutyNotifier extends AbstractNotifier {
31+
public static final URI DEFAULT_URI = URI
32+
.create("https://events.pagerduty.com/generic/2010-04-15/create_event.json");
33+
34+
private final static String DEFAULT_DESCRIPTION = "#{application.name}/#{application.id} is #{to.status}";
35+
36+
private final SpelExpressionParser parser = new SpelExpressionParser();
37+
private RestTemplate restTemplate = new RestTemplate();
38+
39+
/**
40+
* URI for pagerduty-REST-API
41+
*/
42+
private URI url = DEFAULT_URI;
43+
44+
/**
45+
* Service-Key for pagerduty-REST-API
46+
*/
47+
private String serviceKey;
48+
49+
/**
50+
* Client for pagerduty-REST-API
51+
*/
52+
private String client;
53+
54+
/**
55+
* Client-url for pagerduty-REST-API
56+
*/
57+
private URI clientUrl;
58+
59+
/**
60+
* Trigger description. SpEL template using event as root;
61+
*/
62+
private Expression description;
63+
64+
public PagerdutyNotifier() {
65+
this.description = parser.parseExpression(DEFAULT_DESCRIPTION,
66+
ParserContext.TEMPLATE_EXPRESSION);
67+
}
68+
69+
@Override
70+
protected void notify(ClientApplicationStatusChangedEvent event) throws Exception {
71+
restTemplate.postForEntity(url, createPagerdutyEvent(event), Void.class);
72+
}
73+
74+
private Map<String, Object> createPagerdutyEvent(ClientApplicationStatusChangedEvent event) {
75+
Map<String, Object> result = new HashMap<String, Object>();
76+
result.put("service_key", serviceKey);
77+
result.put("incident_key",
78+
event.getApplication().getName() + "/" + event.getApplication().getId());
79+
result.put("description", description.getValue(event, String.class));
80+
81+
Map<String, Object> details = new HashMap<String, Object>();
82+
details.put("from", event.getFrom());
83+
details.put("to", event.getTo());
84+
result.put("details", details);
85+
86+
if ("UP".equals(event.getTo().getStatus())) {
87+
result.put("event_type", "resolve");
88+
89+
} else {
90+
result.put("event_type", "trigger");
91+
if (client != null) {
92+
result.put("client", client);
93+
}
94+
if (clientUrl != null) {
95+
result.put("client_url", clientUrl);
96+
}
97+
98+
Map<String, Object> context = new HashMap<String, Object>();
99+
context.put("type", "link");
100+
context.put("href", event.getApplication().getHealthUrl());
101+
context.put("text", "Application health-endpoint");
102+
result.put("contexts", Arrays.asList(context));
103+
}
104+
105+
return result;
106+
}
107+
108+
public void setUrl(URI url) {
109+
this.url = url;
110+
}
111+
112+
public void setClient(String client) {
113+
this.client = client;
114+
}
115+
116+
public void setClientUrl(URI clientUrl) {
117+
this.clientUrl = clientUrl;
118+
}
119+
120+
public void setServiceKey(String serviceKey) {
121+
this.serviceKey = serviceKey;
122+
}
123+
124+
public void setDescription(String description) {
125+
this.description = parser.parseExpression(description, ParserContext.TEMPLATE_EXPRESSION);
126+
}
127+
128+
public void setRestTemplate(RestTemplate restTemplate) {
129+
this.restTemplate = restTemplate;
130+
}
131+
}

spring-boot-admin-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
{"groups": [
2-
{
3-
"name": "spring.boot.admin.hazelcast",
4-
"sourceType": "de.codecentric.boot.admin.config.HazelcastStoreConfiguration"
5-
},
6-
{
7-
"name": "spring.boot.admin.discovery",
8-
"sourceType": "de.codecentric.boot.admin.config.DiscoveryClientConfiguration"
9-
}
2+
103
],"properties": [
114
{
125
"name": "spring.boot.admin.hazelcast.enabled",

0 commit comments

Comments
 (0)