Suppose you have a Slack account

Preview

slack_window

Integration

Add integration

slack_add_apps

Search & add integration

search_app

Add configuration

add_configuration

Select channel

select_channel

Copy webhook URL & save settings

webhook_url

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<include resource="org/springframework/boot/logging/logback/base.xml"/>
	<appender name="SLACK" class="io.github.piaozaiguang.logback.LogbackSlackAppender">
        <level>ERROR</level>
        <endpoint>https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XxxxxXXxXXXxxxxXXXXXxxxX</endpoint>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>[${HOSTNAME}][Admin RELEASE] %date %-5level - %logger{0} - %message%n</pattern>
        </layout>
	</appender>

	<!-- Root Logger -->
	<root level="ALL">
		<appender-ref ref="CONSOLE" />
		<appender-ref ref="FILE" />
		<appender-ref ref="SLACK" />
	</root>
</configuration>

LogbackSlackAppender.java

package io.github.piaozaiguang.logback;

import org.apache.commons.lang.StringUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Layout;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import ch.qos.logback.core.status.ErrorStatus;
import lombok.Getter;
import lombok.Setter;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;

/**
 * Created by piaozaiguang on 2017/4/8.
 */
@Getter
@Setter
@Slf4j
public class LogbackSlackAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
    private static final String SLACK_LOGGING = "slack logging response: ";
    private static final String SLACK_LOGGING_RESPONSE = SLACK_LOGGING + "{}";
    private String endpoint;
    private Level level;
    private Layout<ILoggingEvent> layout;

    @Override
    public void start() {
        int errors = 0;
        if (level == null) {
            addStatus(new ErrorStatus("No level set for the appender named \"" + name + "\".", this));
            errors++;
        }
        if (endpoint == null) {
            addStatus(new ErrorStatus("No endpoint set for the appender named \"" + name + "\".", this));
            errors++;
        }
        if (layout == null) {
            addStatus(new ErrorStatus("No layout set for the appender named \"" + name + "\".", this));
            errors++;
        }
        if (errors == 0) {
            super.start();
        }
    };

    @Override
    protected void append(ILoggingEvent evt) {
        if (!isStarted()) {
            return;
        }

        if (!StringUtils.contains(evt.getMessage(), SLACK_LOGGING)) {
            if (evt.getLevel().isGreaterOrEqual(level)) {
                Message message = new Message(layout.doLayout(evt));
                RestTemplate rt = new RestTemplate();
                ResponseEntity<String> response = postMessage(message, rt);
                log.debug(SLACK_LOGGING_RESPONSE, response);
            }
        }
    }

    protected ResponseEntity<String> postMessage(Message message, RestTemplate rt) {
        ResponseEntity<String> response = rt.postForEntity(endpoint, message, String.class);
        return response;
    }

    @Value
    static class Message {
        private String text;
    }
}