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

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


The following commit(s) were added to refs/heads/master by this push:
     new 6671f4603b [fix] never send null template variables to Aliyun SMS 
(#4331)
6671f4603b is described below

commit 6671f4603bc9446b70545cdfeaadd9cfb1060efe
Author: NekoPunch <[email protected]>
AuthorDate: Sun Aug 23 03:49:55 2026 -0700

    [fix] never send null template variables to Aliyun SMS (#4331)
    
    Co-authored-by: aias00 <[email protected]>
---
 .../alert/service/impl/AlibabaSmsClientImpl.java   | 39 ++++++------
 .../service/impl/NoticeConfigServiceImpl.java      |  3 +-
 .../service/impl/AlibabaSmsClientImplTest.java     | 74 ++++++++++++++++++++++
 home/docs/help/alert_sms.md                        |  4 +-
 .../current/help/alert_sms.md                      |  4 +-
 5 files changed, 101 insertions(+), 23 deletions(-)

diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlibabaSmsClientImpl.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlibabaSmsClientImpl.java
index 1876d53940..2fdc05f812 100644
--- 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlibabaSmsClientImpl.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/AlibabaSmsClientImpl.java
@@ -78,27 +78,30 @@ public class AlibabaSmsClientImpl implements SmsClient {
 
     @Override
     public void sendMessage(NoticeReceiver receiver, NoticeTemplate 
noticeTemplate, GroupAlert alert) {
-        // Extract alert info
-        String instance = null;
-        String priority = null;
-        String content = null;
-        if (alert.getCommonLabels() != null) {
-            instance = alert.getCommonLabels().get("instance");
-            priority = alert.getCommonLabels().get("priority");
-            content = alert.getCommonAnnotations().get("summary");
-            content = content == null ? 
alert.getCommonAnnotations().get("description") : content;
-            if (content == null) {
-                content = 
alert.getCommonAnnotations().values().stream().findFirst().orElse(null);
-            }
-        }
+        sendSms(receiver.getPhone(), buildTemplateParam(alert));
+    }
+
+    // Aliyun rejects the whole request when any template variable is null or 
blank,
+    // so every value must fall back to non-blank text
+    String buildTemplateParam(GroupAlert alert) {
+        Map<String, String> labels = alert.getCommonLabels() == null ? 
Map.of() : alert.getCommonLabels();
+        Map<String, String> annotations = alert.getCommonAnnotations() == null 
? Map.of() : alert.getCommonAnnotations();
 
-        // Build template parameters
         Map<String, String> templateParam = new HashMap<>();
-        templateParam.put("instance", instance == null ? alert.getGroupKey() : 
instance);
-        templateParam.put("priority", priority == null ? "unknown" : priority);
-        templateParam.put("content", content);
+        templateParam.put("instance", firstNonBlank(labels.get("instance"), 
alert.getGroupKey(), "unknown"));
+        templateParam.put("priority", firstNonBlank(labels.get("priority"), 
"unknown"));
+        templateParam.put("content", firstNonBlank(annotations.get("summary"), 
annotations.get("description"),
+                annotations.values().stream().findFirst().orElse(null), "alert 
triggered"));
+        return JsonUtil.toJson(templateParam);
+    }
 
-        sendSms(receiver.getPhone(), JsonUtil.toJson(templateParam));
+    private static String firstNonBlank(String... values) {
+        for (String value : values) {
+            if (value != null && !value.isBlank()) {
+                return value;
+            }
+        }
+        return "unknown";
     }
 
     private void sendSms(String phoneNumber, String templateParam) {
diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/NoticeConfigServiceImpl.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/NoticeConfigServiceImpl.java
index 8469315c5d..7399146636 100644
--- 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/NoticeConfigServiceImpl.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/NoticeConfigServiceImpl.java
@@ -377,7 +377,8 @@ public class NoticeConfigServiceImpl implements 
NoticeConfigService, CommandLine
             .status("firing")
             .build();
         GroupAlert groupAlert = GroupAlert.builder()
-            .commonLabels(Map.of(CommonConstants.LABEL_ALERT_NAME, "CPU Usage 
Alert"))
+            .commonLabels(Map.of(CommonConstants.LABEL_ALERT_NAME, "CPU Usage 
Alert",
+                CommonConstants.LABEL_INSTANCE, "127.0.0.1"))
             .commonAnnotations(annotations)
             .alerts(List.of(singleAlert1, singleAlert2))
             .status("firing")
diff --git 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/impl/AlibabaSmsClientImplTest.java
 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/impl/AlibabaSmsClientImplTest.java
new file mode 100644
index 0000000000..8ef633860f
--- /dev/null
+++ 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/impl/AlibabaSmsClientImplTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.hertzbeat.alert.service.impl;
+
+import java.util.Map;
+import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
+import org.apache.hertzbeat.common.util.JsonUtil;
+import org.junit.jupiter.api.Test;
+import tools.jackson.core.type.TypeReference;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test case for {@link AlibabaSmsClientImpl}: Aliyun rejects a template 
variable
+ * whose value is null or blank, so the built param JSON must never contain 
one.
+ */
+class AlibabaSmsClientImplTest {
+
+    private final AlibabaSmsClientImpl client = new AlibabaSmsClientImpl(null);
+
+    private Map<String, String> params(GroupAlert alert) {
+        return JsonUtil.fromJson(client.buildTemplateParam(alert), new 
TypeReference<>() { });
+    }
+
+    @Test
+    void testAlertShapedLikeSendTestMsgHasNoNullVariables() {
+        GroupAlert alert = GroupAlert.builder()
+                .commonLabels(Map.of("alertname", "CPU Usage Alert"))
+                .commonAnnotations(Map.of("suggest", "Please check the CPU 
usage of the server"))
+                .build();
+
+        Map<String, String> params = params(alert);
+        assertEquals(3, params.size());
+        params.forEach((k, v) -> assertFalse(v == null || v.isBlank(), k + " 
must not be null/blank"));
+        assertEquals("unknown", params.get("instance"));
+    }
+
+    @Test
+    void alertWithoutLabelsAndAnnotationsHasNoNullVariables() {
+        Map<String, String> params = params(GroupAlert.builder().build());
+        params.forEach((k, v) -> assertFalse(v == null || v.isBlank(), k + " 
must not be null/blank"));
+    }
+
+    @Test
+    void realAlertValuesPassThrough() {
+        GroupAlert alert = GroupAlert.builder()
+                .commonLabels(Map.of("instance", "192.168.1.10:3306", 
"priority", "critical"))
+                .commonAnnotations(Map.of("summary", "mysql down"))
+                .build();
+
+        Map<String, String> params = params(alert);
+        assertEquals("192.168.1.10:3306", params.get("instance"));
+        assertEquals("critical", params.get("priority"));
+        assertEquals("mysql down", params.get("content"));
+        assertTrue(params.values().stream().noneMatch(String::isBlank));
+    }
+}
diff --git a/home/docs/help/alert_sms.md b/home/docs/help/alert_sms.md
index cf2b0e8d46..7c2ba190dc 100644
--- a/home/docs/help/alert_sms.md
+++ b/home/docs/help/alert_sms.md
@@ -81,10 +81,10 @@ alerter:
 3. Create a message template (template-code)
    - Go to "Template Management" page
    - Click "Add Template"
-   - Create a template with the following format:
+   - Create a template with the following content (Aliyun placeholders use the 
`${...}` syntax, and the template must declare exactly these three variables — 
no more, no less — otherwise sending fails with a "template 
variable/placeholder mismatch" error):
 
    ```text
-   Monitor: `instance`, Alert Level: `priority`. Content: `content`
+   Monitor: ${instance}, Alert Level: ${priority}. Content: ${content}
    ```
 
    - Submit the template for review
diff --git 
a/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/alert_sms.md 
b/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/alert_sms.md
index c40a7b8e2b..8ae126e4ab 100644
--- a/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/alert_sms.md
+++ b/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/help/alert_sms.md
@@ -80,10 +80,10 @@ alerter:
 3. 创建短信模板(template-code)
    - 进入"模板管理"页面
    - 点击"添加模板"
-   - 创建如下格式的模板:
+   - 创建如下内容的模板(阿里云占位符语法为 `${...}`,且模板必须恰好声明这三个变量——多一个少一个都会报"模板变量与占位符不一致"错误):
 
    ```text
-   监控项:`instance`,告警级别:`priority`。内容:`content`
+   监控项:${instance},告警级别:${priority}。内容:${content}
    ```
 
    - 提交模板等待审核


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to