You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2020/10/05 12:16:41 UTC

[skywalking] branch master updated: Support Alarm to dingtalk (#5628)

This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git


The following commit(s) were added to refs/heads/master by this push:
     new 117afff  Support Alarm to dingtalk (#5628)
117afff is described below

commit 117afff36fc2bbfafeeb9c02f584ef45539c12b5
Author: xbkaishui <xb...@126.com>
AuthorDate: Mon Oct 5 20:16:18 2020 +0800

    Support Alarm to dingtalk (#5628)
    
    * Add dingtalk webhook
    
    Co-authored-by: 吴晟 Wu Sheng <wu...@foxmail.com>
---
 docs/en/setup/backend/backend-alarm.md             |  18 ++
 .../core/alarm/provider/AlarmRulesWatcher.java     |   6 +
 .../server/core/alarm/provider/NotifyHandler.java  |   2 +
 .../oap/server/core/alarm/provider/Rules.java      |   2 +
 .../server/core/alarm/provider/RulesReader.java    |  34 +++-
 .../provider/dingtalk/DingtalkHookCallback.java    | 154 +++++++++++++++++
 .../{Rules.java => dingtalk/DingtalkSettings.java} |  39 +++--
 .../core/alarm/provider/AlarmRulesWatcherTest.java |   3 +
 ...AlarmRuleInitTest.java => RulesReaderTest.java} |  47 +++++-
 .../dingtalk/DingtalkHookCallbackTest.java         | 184 +++++++++++++++++++++
 .../src/test/resources/alarm-settings.yml          |  36 ++++
 .../src/main/resources/alarm-settings.yml          |  12 ++
 12 files changed, 510 insertions(+), 27 deletions(-)

diff --git a/docs/en/setup/backend/backend-alarm.md b/docs/en/setup/backend/backend-alarm.md
index 345d7cd..e3a730d 100644
--- a/docs/en/setup/backend/backend-alarm.md
+++ b/docs/en/setup/backend/backend-alarm.md
@@ -219,6 +219,24 @@ wechatHooks:
     - https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=dummy_key
 ```
 
+## Dingtalk Hook
+To do this you need to follow the [Dingtalk Webhooks guide](https://ding-doc.dingtalk.com/doc#/serverapi2/qf2nxq/uKPlK) and create new Webhooks.
+For security issue, you can config optional secret for individual webhook url.
+The alarm message will send through HTTP post by `application/json` content type if you configured Dingtalk Webhooks as following:
+```yml
+dingtalkHooks:
+  textTemplate: |-
+    {
+      "msgtype": "text",
+      "text": {
+        "content": "Apache SkyWalking Alarm: \n %s."
+      }
+    }
+  webhooks:
+    - url: https://oapi.dingtalk.com/robot/send?access_token=dummy_token
+      secret: dummysecret
+```
+
 
 ## Update the settings dynamically
 Since 6.5.0, the alarm settings can be updated dynamically at runtime by [Dynamic Configuration](dynamic-config.md),
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcher.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcher.java
index c312b45..af6364a 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcher.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcher.java
@@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.skywalking.oap.server.configuration.api.ConfigChangeWatcher;
 import org.apache.skywalking.oap.server.core.Const;
 import org.apache.skywalking.oap.server.core.alarm.AlarmModule;
+import org.apache.skywalking.oap.server.core.alarm.provider.dingtalk.DingtalkSettings;
 import org.apache.skywalking.oap.server.core.alarm.provider.expression.Expression;
 import org.apache.skywalking.oap.server.core.alarm.provider.expression.ExpressionContext;
 import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCAlarmSetting;
@@ -128,4 +129,9 @@ public class AlarmRulesWatcher extends ConfigChangeWatcher {
     public WechatSettings getWechatSettings() {
         return this.rules.getWecchats();
     }
+
+    public DingtalkSettings getDingtalkSettings() {
+        return this.rules.getDingtalks();
+    }
+
 }
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/NotifyHandler.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/NotifyHandler.java
index 2f95b5c..755bc08 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/NotifyHandler.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/NotifyHandler.java
@@ -31,6 +31,7 @@ import org.apache.skywalking.oap.server.core.alarm.MetaInAlarm;
 import org.apache.skywalking.oap.server.core.alarm.MetricsNotify;
 import org.apache.skywalking.oap.server.core.alarm.ServiceInstanceMetaInAlarm;
 import org.apache.skywalking.oap.server.core.alarm.ServiceMetaInAlarm;
+import org.apache.skywalking.oap.server.core.alarm.provider.dingtalk.DingtalkHookCallback;
 import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCCallback;
 import org.apache.skywalking.oap.server.core.alarm.provider.slack.SlackhookCallback;
 import org.apache.skywalking.oap.server.core.alarm.provider.wechat.WechatHookCallback;
@@ -162,6 +163,7 @@ public class NotifyHandler implements MetricsNotify {
         allCallbacks.add(new GRPCCallback(alarmRulesWatcher));
         allCallbacks.add(new SlackhookCallback(alarmRulesWatcher));
         allCallbacks.add(new WechatHookCallback(alarmRulesWatcher));
+        allCallbacks.add(new DingtalkHookCallback(alarmRulesWatcher));
         core.start(allCallbacks);
     }
 }
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java
index 04ba1b1..7831bd6 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java
@@ -23,6 +23,7 @@ import java.util.List;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
+import org.apache.skywalking.oap.server.core.alarm.provider.dingtalk.DingtalkSettings;
 import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCAlarmSetting;
 import org.apache.skywalking.oap.server.core.alarm.provider.slack.SlackSettings;
 import org.apache.skywalking.oap.server.core.alarm.provider.wechat.WechatSettings;
@@ -37,6 +38,7 @@ public class Rules {
     private SlackSettings slacks;
     private WechatSettings wecchats;
     private List<CompositeAlarmRule> compositeRules;
+    private DingtalkSettings dingtalks;
 
     public Rules() {
         this.rules = new ArrayList<>();
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReader.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReader.java
index 9290f2f..350254e 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReader.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReader.java
@@ -18,6 +18,12 @@
 
 package org.apache.skywalking.oap.server.core.alarm.provider;
 
+import org.apache.skywalking.oap.server.core.alarm.provider.dingtalk.DingtalkSettings;
+import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCAlarmSetting;
+import org.apache.skywalking.oap.server.core.alarm.provider.slack.SlackSettings;
+import org.apache.skywalking.oap.server.core.alarm.provider.wechat.WechatSettings;
+import org.yaml.snakeyaml.Yaml;
+
 import java.io.InputStream;
 import java.io.Reader;
 import java.util.ArrayList;
@@ -25,11 +31,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
-import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCAlarmSetting;
-import org.apache.skywalking.oap.server.core.alarm.provider.slack.SlackSettings;
-import org.apache.skywalking.oap.server.core.alarm.provider.wechat.WechatSettings;
-import org.yaml.snakeyaml.Yaml;
-
 /**
  * Rule Reader parses the given `alarm-settings.yml` config file, to the target {@link Rules}.
  */
@@ -59,10 +60,12 @@ public class RulesReader {
             readSlackConfig(rules);
             readWechatConfig(rules);
             readCompositeRuleConfig(rules);
+            readDingtalkConfig(rules);
         }
         return rules;
     }
 
+
     /**
      * Read rule config into {@link AlarmRule}
      */
@@ -203,4 +206,25 @@ public class RulesReader {
             }
         });
     }
+
+    /**
+     * Read dingtalk hook config into {@link DingtalkSettings}
+     */
+    private void readDingtalkConfig(Rules rules) {
+        Map dingtalkConfig = (Map) yamlData.get("dingtalkHooks");
+        if (dingtalkConfig != null) {
+            DingtalkSettings dingtalkSettings = new DingtalkSettings();
+            Object textTemplate = dingtalkConfig.getOrDefault("textTemplate", "");
+            dingtalkSettings.setTextTemplate((String) textTemplate);
+            List<Map<String, Object>> wechatWebhooks = (List<Map<String, Object>>) dingtalkConfig.get("webhooks");
+            if (wechatWebhooks != null) {
+                wechatWebhooks.forEach(wechatWebhook -> {
+                    Object secret = wechatWebhook.getOrDefault("secret", "");
+                    Object url = wechatWebhook.getOrDefault("url", "");
+                    dingtalkSettings.getWebhooks().add(new DingtalkSettings.WebHookUrl((String) secret, (String) url));
+                });
+            }
+            rules.setDingtalks(dingtalkSettings);
+        }
+    }
 }
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkHookCallback.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkHookCallback.java
new file mode 100644
index 0000000..5acc63a
--- /dev/null
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkHookCallback.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.core.alarm.provider.dingtalk;
+
+import io.netty.handler.codec.http.HttpHeaderValues;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpStatus;
+import org.apache.http.StatusLine;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.apache.skywalking.apm.util.StringUtil;
+import org.apache.skywalking.oap.server.core.alarm.AlarmCallback;
+import org.apache.skywalking.oap.server.core.alarm.AlarmMessage;
+import org.apache.skywalking.oap.server.core.alarm.provider.AlarmRulesWatcher;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Base64;
+import java.util.List;
+
+/**
+ * Use SkyWalking alarm dingtalk webhook API.
+ */
+@Slf4j
+public class DingtalkHookCallback implements AlarmCallback {
+
+    private static final int HTTP_CONNECT_TIMEOUT = 1000;
+    private static final int HTTP_CONNECTION_REQUEST_TIMEOUT = 1000;
+    private static final int HTTP_SOCKET_TIMEOUT = 10000;
+    private AlarmRulesWatcher alarmRulesWatcher;
+    private RequestConfig requestConfig;
+
+    public DingtalkHookCallback(final AlarmRulesWatcher alarmRulesWatcher) {
+        this.alarmRulesWatcher = alarmRulesWatcher;
+        this.requestConfig = RequestConfig.custom()
+                .setConnectTimeout(HTTP_CONNECT_TIMEOUT)
+                .setConnectionRequestTimeout(HTTP_CONNECTION_REQUEST_TIMEOUT)
+                .setSocketTimeout(HTTP_SOCKET_TIMEOUT)
+                .build();
+    }
+
+    /**
+     * Send alarm message if the settings not empty
+     */
+    @Override
+    public void doAlarm(List<AlarmMessage> alarmMessages) {
+        if (this.alarmRulesWatcher.getDingtalkSettings() == null || this.alarmRulesWatcher.getDingtalkSettings().getWebhooks().isEmpty()) {
+            return;
+        }
+        CloseableHttpClient httpClient = HttpClients.custom().build();
+        try {
+            DingtalkSettings dingtalkSettings = this.alarmRulesWatcher.getDingtalkSettings();
+            dingtalkSettings.getWebhooks().forEach(webHookUrl -> {
+                alarmMessages.forEach(alarmMessage -> {
+                    String url = getUrl(webHookUrl);
+                    String requestBody = String.format(
+                            this.alarmRulesWatcher.getDingtalkSettings().getTextTemplate(), alarmMessage.getAlarmMessage()
+                    );
+                    sendAlarmMessage(httpClient, url, requestBody);
+                });
+            });
+        } finally {
+            try {
+                httpClient.close();
+            } catch (IOException e) {
+                log.error(e.getMessage(), e);
+            }
+        }
+    }
+
+    /**
+     * Get webhook url, sign the url when secret is not empty.
+     */
+    private String getUrl(DingtalkSettings.WebHookUrl webHookUrl) {
+        if (StringUtil.isEmpty(webHookUrl.getSecret())) {
+            return webHookUrl.getUrl();
+        }
+        return getSignUrl(webHookUrl);
+    }
+
+    /**
+     * Sign webhook url using secret and timestamp
+     */
+    private String getSignUrl(DingtalkSettings.WebHookUrl webHookUrl) {
+        try {
+            Long timestamp = System.currentTimeMillis();
+            return String.format("%s&timestamp=%s&sign=%s", webHookUrl.getUrl(), timestamp, sign(timestamp, webHookUrl.getSecret()));
+        } catch (NoSuchAlgorithmException | UnsupportedEncodingException | InvalidKeyException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Sign webhook url using HmacSHA256 algorithm
+     */
+    private String sign(final Long timestamp, String secret) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
+        String stringToSign = timestamp + "\n" + secret;
+        Mac mac = Mac.getInstance("HmacSHA256");
+        mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
+        byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
+        return URLEncoder.encode(new String(Base64.getEncoder().encode(signData)), StandardCharsets.UTF_8.name());
+    }
+
+    /**
+     * Send alarm message to remote endpoint
+     */
+    private void sendAlarmMessage(CloseableHttpClient httpClient, String url, String requestBody) {
+        try {
+            HttpPost post = new HttpPost(url);
+            post.setConfig(requestConfig);
+            post.setHeader(HttpHeaders.ACCEPT, HttpHeaderValues.APPLICATION_JSON.toString());
+            post.setHeader(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON.toString());
+            StringEntity entity = new StringEntity(requestBody, ContentType.APPLICATION_JSON);
+            post.setEntity(entity);
+            CloseableHttpResponse httpResponse = httpClient.execute(post);
+            StatusLine statusLine = httpResponse.getStatusLine();
+            if (statusLine != null && statusLine.getStatusCode() != HttpStatus.SC_OK) {
+                log.error("send dingtalk alarm to {} failure. Response code: {}, Response content: {}", url, statusLine.getStatusCode(),
+                        EntityUtils.toString(httpResponse.getEntity()));
+            }
+        } catch (Throwable e) {
+            log.error("send dingtalk alarm to {} failure.", url, e);
+        }
+    }
+}
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkSettings.java
similarity index 57%
copy from oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java
copy to oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkSettings.java
index 04ba1b1..ba1d750 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/Rules.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkSettings.java
@@ -16,31 +16,36 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core.alarm.provider;
+package org.apache.skywalking.oap.server.core.alarm.provider.dingtalk;
 
-import java.util.ArrayList;
-import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Getter;
+import lombok.NoArgsConstructor;
 import lombok.Setter;
 import lombok.ToString;
-import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCAlarmSetting;
-import org.apache.skywalking.oap.server.core.alarm.provider.slack.SlackSettings;
-import org.apache.skywalking.oap.server.core.alarm.provider.wechat.WechatSettings;
 
+import java.util.ArrayList;
+import java.util.List;
+
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
 @Setter
 @Getter
 @ToString
-public class Rules {
-    private List<AlarmRule> rules;
-    private List<String> webhooks;
-    private GRPCAlarmSetting grpchookSetting;
-    private SlackSettings slacks;
-    private WechatSettings wecchats;
-    private List<CompositeAlarmRule> compositeRules;
+public class DingtalkSettings {
+
+    private String textTemplate;
+    @Builder.Default
+    private List<WebHookUrl> webhooks = new ArrayList<>();
 
-    public Rules() {
-        this.rules = new ArrayList<>();
-        this.webhooks = new ArrayList<>();
-        this.compositeRules = new ArrayList<>();
+    @AllArgsConstructor
+    @Setter
+    @Getter
+    @ToString
+    public static class WebHookUrl {
+        private final String secret;
+        private final String url;
     }
 }
diff --git a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcherTest.java b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcherTest.java
index c2ed807..e9277a6 100644
--- a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcherTest.java
+++ b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRulesWatcherTest.java
@@ -82,6 +82,9 @@ public class AlarmRulesWatcherTest {
         assertNotNull(alarmRulesWatcher.getGrpchookSetting());
         assertEquals(9888, alarmRulesWatcher.getGrpchookSetting().getTargetPort());
         assertEquals(2, alarmRulesWatcher.getRunningContext().size());
+        assertNotNull(alarmRulesWatcher.getDingtalkSettings());
+        assertNotNull(alarmRulesWatcher.getWechatSettings());
+        assertNotNull(alarmRulesWatcher.getSlackSettings());
     }
 
     @Test
diff --git a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRuleInitTest.java b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReaderTest.java
similarity index 50%
rename from oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRuleInitTest.java
rename to oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReaderTest.java
index 9541476..9a81990 100644
--- a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRuleInitTest.java
+++ b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReaderTest.java
@@ -18,16 +18,27 @@
 
 package org.apache.skywalking.oap.server.core.alarm.provider;
 
-import java.util.List;
+import org.apache.skywalking.oap.server.core.alarm.provider.dingtalk.DingtalkSettings;
+import org.apache.skywalking.oap.server.core.alarm.provider.grpc.GRPCAlarmSetting;
+import org.apache.skywalking.oap.server.core.alarm.provider.slack.SlackSettings;
+import org.apache.skywalking.oap.server.core.alarm.provider.wechat.WechatSettings;
 import org.junit.Assert;
 import org.junit.Test;
 
-public class AlarmRuleInitTest {
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.any;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+
+public class RulesReaderTest {
     @Test
-    public void testInit() {
+    public void testReadRules() {
         RulesReader reader = new RulesReader(this.getClass()
-                                                 .getClassLoader()
-                                                 .getResourceAsStream("alarm-settings.yml"));
+                .getClassLoader()
+                .getResourceAsStream("alarm-settings.yml"));
         Rules rules = reader.readRules();
 
         List<AlarmRule> ruleList = rules.getRules();
@@ -46,8 +57,34 @@ public class AlarmRuleInitTest {
         Assert.assertEquals(2, rulesWebhooks.size());
         Assert.assertEquals("http://127.0.0.1/go-wechat/", rulesWebhooks.get(1));
 
+        GRPCAlarmSetting grpcAlarmSetting = rules.getGrpchookSetting();
+        assertNotNull(grpcAlarmSetting);
+        assertThat(grpcAlarmSetting.getTargetHost(), is("127.0.0.1"));
+        assertThat(grpcAlarmSetting.getTargetPort(), is(9888));
+
+        SlackSettings slackSettings = rules.getSlacks();
+        assertNotNull(slackSettings);
+        assertThat(slackSettings.getWebhooks().size(), is(1));
+        assertThat(slackSettings.getWebhooks().get(0), is("https://hooks.slack.com/services/x/y/zssss"));
+        assertThat(slackSettings.getTextTemplate(), any(String.class));
+
+        WechatSettings wechatSettings = rules.getWecchats();
+        assertNotNull(wechatSettings);
+        assertThat(wechatSettings.getWebhooks().size(), is(1));
+        assertThat(wechatSettings.getWebhooks().get(0), is("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=dummy_key"));
+        assertThat(slackSettings.getTextTemplate(), any(String.class));
+
         List<CompositeAlarmRule> compositeRules = rules.getCompositeRules();
         Assert.assertEquals(1, compositeRules.size());
         Assert.assertEquals("endpoint_percent_more_rule && endpoint_percent_rule", compositeRules.get(0).getExpression());
+
+        DingtalkSettings dingtalkSettings = rules.getDingtalks();
+        assertThat(dingtalkSettings.getTextTemplate(), any(String.class));
+        List<DingtalkSettings.WebHookUrl> webHookUrls = dingtalkSettings.getWebhooks();
+        assertThat(webHookUrls.size(), is(2));
+        assertThat(webHookUrls.get(0).getUrl(), is("https://oapi.dingtalk.com/robot/send?access_token=dummy_token"));
+        assertThat(webHookUrls.get(0).getSecret(), is("dummysecret"));
+        assertThat(webHookUrls.get(1).getUrl(), is("https://oapi.dingtalk.com/robot/send?access_token=dummy_token2"));
+        assertNull(webHookUrls.get(1).getSecret());
     }
 }
diff --git a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkHookCallbackTest.java b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkHookCallbackTest.java
new file mode 100644
index 0000000..e092ed9
--- /dev/null
+++ b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/dingtalk/DingtalkHookCallbackTest.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.core.alarm.provider.dingtalk;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import org.apache.skywalking.apm.util.StringUtil;
+import org.apache.skywalking.oap.server.core.alarm.AlarmMessage;
+import org.apache.skywalking.oap.server.core.alarm.provider.AlarmRulesWatcher;
+import org.apache.skywalking.oap.server.core.alarm.provider.Rules;
+import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+
+public class DingtalkHookCallbackTest implements Servlet {
+
+    private Server server;
+    private int port;
+    private volatile boolean isSuccess = false;
+    private int count;
+    private volatile boolean checkSign = false;
+    private final String secret = "dummy-secret";
+
+    @Before
+    public void init() throws Exception {
+        server = new Server(new InetSocketAddress("127.0.0.1", 0));
+        ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
+        servletContextHandler.setContextPath("/dingtalkhook");
+        server.setHandler(servletContextHandler);
+        ServletHolder servletHolder = new ServletHolder();
+        servletHolder.setServlet(this);
+        servletContextHandler.addServlet(servletHolder, "/receiveAlarm");
+        server.start();
+        port = server.getURI().getPort();
+        assertTrue(port > 0);
+    }
+
+    @Test
+    public void testDingtalkWebhookWithoutSign() {
+        List<DingtalkSettings.WebHookUrl> webHooks = new ArrayList<>();
+        webHooks.add(new DingtalkSettings.WebHookUrl("", "http://127.0.0.1:" + port + "/dingtalkhook/receiveAlarm?token=dummy_token"));
+        Rules rules = new Rules();
+        String template = "{\"msgtype\":\"text\",\"text\":{\"content\":\"Skywaling alarm: %s\"}}";
+        rules.setDingtalks(DingtalkSettings.builder().webhooks(webHooks).textTemplate(template).build());
+
+        AlarmRulesWatcher alarmRulesWatcher = new AlarmRulesWatcher(rules, null);
+        DingtalkHookCallback dingtalkCallBack = new DingtalkHookCallback(alarmRulesWatcher);
+        List<AlarmMessage> alarmMessages = new ArrayList<>(2);
+        AlarmMessage alarmMessage = new AlarmMessage();
+        alarmMessage.setScopeId(DefaultScopeDefine.ALL);
+        alarmMessage.setRuleName("service_resp_time_rule");
+        alarmMessage.setAlarmMessage("alarmMessage with [DefaultScopeDefine.All]");
+        alarmMessages.add(alarmMessage);
+        AlarmMessage anotherAlarmMessage = new AlarmMessage();
+        anotherAlarmMessage.setRuleName("service_resp_time_rule_2");
+        anotherAlarmMessage.setScopeId(DefaultScopeDefine.ENDPOINT);
+        anotherAlarmMessage.setAlarmMessage("anotherAlarmMessage with [DefaultScopeDefine.Endpoint]");
+        alarmMessages.add(anotherAlarmMessage);
+        dingtalkCallBack.doAlarm(alarmMessages);
+        Assert.assertTrue(isSuccess);
+    }
+
+    @Test
+    public void testDingtalkWebhookWithSign() {
+        checkSign = true;
+        List<DingtalkSettings.WebHookUrl> webHooks = new ArrayList<>();
+        webHooks.add(new DingtalkSettings.WebHookUrl(secret, "http://127.0.0.1:" + port + "/dingtalkhook/receiveAlarm?token=dummy_token"));
+        Rules rules = new Rules();
+        String template = "{\"msgtype\":\"text\",\"text\":{\"content\":\"Skywaling alarm: %s\"}}";
+        rules.setDingtalks(DingtalkSettings.builder().webhooks(webHooks).textTemplate(template).build());
+
+        AlarmRulesWatcher alarmRulesWatcher = new AlarmRulesWatcher(rules, null);
+        DingtalkHookCallback dingtalkCallBack = new DingtalkHookCallback(alarmRulesWatcher);
+        List<AlarmMessage> alarmMessages = new ArrayList<>(2);
+        AlarmMessage alarmMessage = new AlarmMessage();
+        alarmMessage.setScopeId(DefaultScopeDefine.ALL);
+        alarmMessage.setRuleName("service_resp_time_rule");
+        alarmMessage.setAlarmMessage("alarmMessage with [DefaultScopeDefine.All]");
+        alarmMessages.add(alarmMessage);
+        AlarmMessage anotherAlarmMessage = new AlarmMessage();
+        anotherAlarmMessage.setRuleName("service_resp_time_rule_2");
+        anotherAlarmMessage.setScopeId(DefaultScopeDefine.ENDPOINT);
+        anotherAlarmMessage.setAlarmMessage("anotherAlarmMessage with [DefaultScopeDefine.Endpoint]");
+        alarmMessages.add(anotherAlarmMessage);
+        dingtalkCallBack.doAlarm(alarmMessages);
+        Assert.assertTrue(isSuccess);
+    }
+
+    @After
+    public void stop() throws Exception {
+        server.stop();
+    }
+
+    @Override
+    public void init(ServletConfig servletConfig) throws ServletException {
+    }
+
+    @Override
+    public ServletConfig getServletConfig() {
+        return null;
+    }
+
+    @Override
+    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
+        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+        if (httpServletRequest.getContentType().equals("application/json")) {
+            InputStream inputStream = request.getInputStream();
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            byte[] buffer = new byte[2048];
+            int readCntOnce;
+
+            while ((readCntOnce = inputStream.read(buffer)) >= 0) {
+                out.write(buffer, 0, readCntOnce);
+            }
+
+            JsonObject jsonObject = new Gson().fromJson(new String(out.toByteArray()), JsonObject.class);
+            String type = jsonObject.get("msgtype").getAsString();
+            if (checkSign) {
+                String timestamp = request.getParameter("timestamp");
+                String sign = request.getParameter("sign");
+                if (StringUtil.isEmpty(timestamp) || StringUtil.isEmpty(sign)) {
+                    ((HttpServletResponse) response).setStatus(500);
+                    return;
+                }
+            }
+            if (type.equalsIgnoreCase("text")) {
+                ((HttpServletResponse) response).setStatus(200);
+                count = count + 1;
+                if (count == 2) {
+                    isSuccess = true;
+                }
+                return;
+            }
+
+            ((HttpServletResponse) response).setStatus(500);
+        }
+    }
+
+    @Override
+    public String getServletInfo() {
+        return null;
+    }
+
+    @Override
+    public void destroy() {
+    }
+}
diff --git a/oap-server/server-alarm-plugin/src/test/resources/alarm-settings.yml b/oap-server/server-alarm-plugin/src/test/resources/alarm-settings.yml
index 4679bcc..9dd339f 100755
--- a/oap-server/server-alarm-plugin/src/test/resources/alarm-settings.yml
+++ b/oap-server/server-alarm-plugin/src/test/resources/alarm-settings.yml
@@ -71,3 +71,39 @@ gRPCHook:
   target_host: 127.0.0.1
   target_port: 9888
 
+slackHooks:
+  textTemplate: |-
+    {
+      "type": "section",
+      "text": {
+        "type": "mrkdwn",
+        "text": ":alarm_clock: *Apache Skywalking Alarm* \n **%s**."
+      }
+    }
+  webhooks:
+    - https://hooks.slack.com/services/x/y/zssss
+
+wechatHooks:
+  textTemplate: |-
+    {
+      "msgtype": "text",
+      "text": {
+        "content": "Apache SkyWalking Alarm: \n %s."
+      }
+    }
+  webhooks:
+    - https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=dummy_key
+
+dingtalkHooks:
+  textTemplate: |-
+    {
+      "msgtype": "text",
+      "text": {
+        "content": "Apache SkyWalking Alarm: \n %s."
+      }
+    }
+  webhooks:
+    - url: https://oapi.dingtalk.com/robot/send?access_token=dummy_token
+      secret: dummysecret
+    - url: https://oapi.dingtalk.com/robot/send?access_token=dummy_token2
+      secret:
diff --git a/oap-server/server-bootstrap/src/main/resources/alarm-settings.yml b/oap-server/server-bootstrap/src/main/resources/alarm-settings.yml
index 00b7ae9..f78427d 100755
--- a/oap-server/server-bootstrap/src/main/resources/alarm-settings.yml
+++ b/oap-server/server-bootstrap/src/main/resources/alarm-settings.yml
@@ -69,3 +69,15 @@ wechatHooks:
     }
   webhooks:
 #    - https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=dummy_key
+
+dingtalkHooks:
+  textTemplate: |-
+    {
+      "msgtype": "text",
+      "text": {
+        "content": "Apache SkyWalking Alarm: \n %s."
+      }
+    }
+  webhooks:
+#    - url: https://oapi.dingtalk.com/robot/send?access_token=dummy_token
+#      secret: dummysecret