sirius.web.health.Slack Maven / Gradle / Ivy
/*
* Made with all the love in the world
* by scireum in Remshalden, Germany
*
* Copyright by scireum GmbH
* http://www.scireum.de - [email protected]
*/
package sirius.web.health;
import com.google.common.collect.Maps;
import sirius.kernel.async.CallContext;
import sirius.kernel.commons.RateLimit;
import sirius.kernel.commons.Strings;
import sirius.kernel.commons.Tuple;
import sirius.kernel.di.std.ConfigValue;
import sirius.kernel.di.std.Register;
import sirius.kernel.health.ExceptionHandler;
import sirius.kernel.health.Exceptions;
import sirius.kernel.health.Incident;
import sirius.kernel.info.Product;
import sirius.web.services.JSONCall;
import sirius.web.services.JSONStructuredOutput;
import javax.annotation.Nullable;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* Helper class to notify an Slack room about certain events.
*/
@Register(classes = {Slack.class, ExceptionHandler.class})
public class Slack implements ExceptionHandler {
@Override
public void handle(Incident incident) throws Exception {
LinkedHashMap fields = Maps.newLinkedHashMap();
fields.put("Category", incident.getCategory());
for (Tuple t : CallContext.getCurrent().getMDC()) {
fields.put(t.getFirst(), t.getSecond());
}
fields.put("Location", incident.getLocation());
fields.put("Product", Product.getProduct().getName() + " (" + Product.getProduct().getDetails() + ")");
sendMessage("incident", incident.getException().getMessage(), Color.DANGER, fields);
}
/**
* Determines which color to use for a message
*/
public enum Color {
GOOD, WARNING, DANGER
}
@ConfigValue("health.slack.messageUrl")
protected String messageUrl;
@ConfigValue("health.slack.channel")
protected String channel;
@ConfigValue("health.slack.sender")
protected String sender;
@ConfigValue("health.slack.icon_url")
protected String iconUrl;
@ConfigValue("health.slack.types")
protected List messageTypes;
protected Set messageFilter;
private RateLimit rateLimit = RateLimit.nTimesPerInterval(15, TimeUnit.SECONDS, 5);
/**
* Sends the given message to Slack (if configured property).
*
* @param messageType determines the type of message to be sent. Only messages with a type listed in the
* config value health.slack.types will be sent, others will be discarded.
* If null is passed in, no filtering is performed and the message is always sent.
* @param message the message to send.
* @param color the color to use
* @param fields the fields to submit
*/
public void sendMessage(@Nullable String messageType,
String message,
Color color,
@Nullable Map fields) {
try {
if (!rateLimit.check()) {
return;
}
if (Strings.isEmpty(messageUrl) || Strings.isEmpty(message)) {
return;
}
if (messageFilter == null) {
messageFilter = messageTypes.stream().map(String::toLowerCase).collect(Collectors.toSet());
}
if (messageType != null && !messageFilter.contains(messageType)) {
return;
}
sendJSONMessage(message, color, fields);
} catch (Exception e) {
Exceptions.handle(e);
}
}
private void sendJSONMessage(String message, Color color, @Nullable Map fields) throws IOException {
JSONCall call = JSONCall.to(new URL(messageUrl));
JSONStructuredOutput out = call.getOutput();
out.beginResult();
out.property("username", Strings.isEmpty(sender) ? CallContext.getNodeName() : sender);
if (Strings.isFilled(channel)) {
out.property("channel", channel);
}
out.property("text", message);
out.beginArray("attachments");
{
out.beginObject("attachment");
{
out.property("color", color.name().toLowerCase());
out.beginArray("fields");
{
if (fields != null) {
for (Map.Entry field : fields.entrySet()) {
if (Strings.isFilled(field.getValue())) {
out.beginObject("field");
{
out.property("title", field.getKey());
out.property("value", field.getValue());
out.property("short", field.getValue().length() <= 50);
}
out.endObject();
}
}
}
}
out.endArray();
}
out.endObject();
}
out.endArray();
out.endResult();
call.getPlainInput();
}
/**
* Same as {@link #sendMessage(String, String, Color, java.util.Map)} but builds
* the field map from a given list of strings. (Name, Value, Name, Value...)
*
* @param messageType determines the type of message to be sent. Only messages with a type listed in the
* config value health.slack.types will be sent, others will be discarded.
* If null is passed in, no filtering is performed and the message is always sent.
* @param message the message to send.
* @param color the color to use
* @param fields the fields to submit as a list like (Name, Value, Name, Value...)
*/
public void sendMessage(@Nullable String messageType, String message, Color color, String... fields) {
Map fieldMap = Maps.newLinkedHashMap();
for (int i = 0; i < fields.length; i += 2) {
if (i + 1 < fields.length) {
fieldMap.put(fields[i], fields[i + 1]);
}
}
sendMessage(messageType, message, color, fieldMap);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy