feat(alerter): support Alibaba Cloud Monitor webhook (#4296)

Co-authored-by: Duansg <siguoduan@gmail.com>
This commit is contained in:
lynx009
2026-08-10 00:41:18 +08:00
committed by GitHub
co-authored by Duansg
parent 42307a9928
commit 3e7c2bc67f
12 changed files with 678 additions and 0 deletions
@@ -0,0 +1,142 @@
/*
* 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.dto;
import com.fasterxml.jackson.annotation.JsonAlias;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Alibaba Cloud Monitor 2.0 webhook alert entity.
*
* @see <a href="https://help.aliyun.com/zh/cms/cloudmonitor-2-0/notification-object">
* Alibaba Cloud Monitor webhook payload fields</a>
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AlibabaCloudCmsExternAlert {
private String specversion;
private String id;
private String type;
private String subtype;
private String source;
private String sourcetype;
private String time;
private Long timestamp;
private String subject;
private String datacontenttype;
private String severity;
private String status;
private String userId;
private String ruleId;
private String workspace;
private String traceId;
private String alertMessage;
private String alertEntityId;
private Resource resource;
private Map<String, Object> labels;
private Map<String, Object> annotations;
private AlertData data;
private Map<String, Object> alertEntityFields;
private String ruleUrl;
private String entityUrl;
private String alertRuleUrl;
private String alertHistoryUrl;
/**
* Alert resource.
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Resource {
private Entity entity;
private Map<String, Object> tags;
}
/**
* Alert resource entity.
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Entity {
private String domain;
@JsonAlias("entity_type")
private String entityType;
@JsonAlias("entity_id")
private String entityId;
private Map<String, Object> prop;
}
/**
* Threshold alert data.
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class AlertData {
private Object value;
private Object threshold;
private String comparisonOperator;
}
}
@@ -0,0 +1,195 @@
/*
* 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.time.Instant;
import java.time.format.DateTimeParseException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.alert.dto.AlibabaCloudCmsExternAlert;
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
import org.apache.hertzbeat.alert.service.ExternAlertService;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.stereotype.Service;
/**
* Alibaba Cloud Monitor 2.0 external alert service.
*/
@Slf4j
@Service
public class AlibabaCloudCmsExternAlertService implements ExternAlertService {
private static final String SOURCE = "alibabacloud-cms";
private final AlarmCommonReduce alarmCommonReduce;
public AlibabaCloudCmsExternAlertService(AlarmCommonReduce alarmCommonReduce) {
this.alarmCommonReduce = alarmCommonReduce;
}
@Override
public void addExternAlert(String content) {
AlibabaCloudCmsExternAlert externAlert = JsonUtil.fromJson(content, AlibabaCloudCmsExternAlert.class);
if (externAlert == null || StringUtils.isBlank(externAlert.getStatus())) {
log.warn("Failed to parse Alibaba Cloud Monitor external alert content: {}", content);
return;
}
alarmCommonReduce.reduceAndSendAlarm(convert(externAlert));
}
@Override
public String supportSource() {
return SOURCE;
}
private SingleAlert convert(AlibabaCloudCmsExternAlert externAlert) {
boolean resolved = isResolved(externAlert);
long eventTime = getEventTime(externAlert);
return SingleAlert.builder()
.content(getAlertContent(externAlert))
.status(resolved ? CommonConstants.ALERT_STATUS_RESOLVED : CommonConstants.ALERT_STATUS_FIRING)
.startAt(eventTime)
.activeAt(resolved ? null : eventTime)
.endAt(resolved ? eventTime : null)
.labels(buildLabels(externAlert))
.annotations(buildAnnotations(externAlert))
.triggerTimes(1)
.build();
}
private boolean isResolved(AlibabaCloudCmsExternAlert externAlert) {
return "RESOLVED".equalsIgnoreCase(externAlert.getStatus())
|| "RECOVERED".equalsIgnoreCase(externAlert.getStatus())
|| "NORMAL_RESOLVE".equalsIgnoreCase(externAlert.getSubtype());
}
private long getEventTime(AlibabaCloudCmsExternAlert externAlert) {
if (externAlert.getTimestamp() != null && externAlert.getTimestamp() > 0) {
return externAlert.getTimestamp();
}
if (StringUtils.isNotBlank(externAlert.getTime())) {
try {
return Instant.parse(externAlert.getTime()).toEpochMilli();
} catch (DateTimeParseException e) {
log.warn("Failed to parse Alibaba Cloud Monitor event time: {}", externAlert.getTime());
}
}
return Instant.now().toEpochMilli();
}
private Map<String, String> buildLabels(AlibabaCloudCmsExternAlert externAlert) {
Map<String, String> labels = new HashMap<>(16);
putValues(labels, externAlert.getLabels());
AlibabaCloudCmsExternAlert.Resource resource = externAlert.getResource();
if (resource != null) {
putValues(labels, resource.getTags());
AlibabaCloudCmsExternAlert.Entity entity = resource.getEntity();
if (entity != null) {
putIfNotBlank(labels, "resourceDomain", entity.getDomain());
putIfNotBlank(labels, "resourceType", entity.getEntityType());
putIfNotBlank(labels, "resourceId", entity.getEntityId());
}
}
labels.put("__source__", SOURCE);
putIfNotBlank(labels, CommonConstants.LABEL_ALERT_NAME, externAlert.getSubject());
putIfNotBlank(labels, CommonConstants.LABEL_ALERT_SEVERITY, convertSeverity(externAlert.getSeverity()));
putIfNotBlank(labels, "ruleId", externAlert.getRuleId());
putIfNotBlank(labels, "workspace", externAlert.getWorkspace());
putIfNotBlank(labels, "alertEntityId", externAlert.getAlertEntityId());
putIfNotBlank(labels, "userId", externAlert.getUserId());
return labels;
}
private Map<String, String> buildAnnotations(AlibabaCloudCmsExternAlert externAlert) {
Map<String, String> annotations = new HashMap<>(16);
putValues(annotations, externAlert.getAnnotations());
AlibabaCloudCmsExternAlert.Resource resource = externAlert.getResource();
if (resource != null && resource.getEntity() != null) {
putValues(annotations, resource.getEntity().getProp());
}
putValues(annotations, externAlert.getAlertEntityFields());
AlibabaCloudCmsExternAlert.AlertData data = externAlert.getData();
if (data != null) {
putValue(annotations, "value", data.getValue());
putValue(annotations, "threshold", data.getThreshold());
putIfNotBlank(annotations, "comparisonOperator", data.getComparisonOperator());
}
putIfNotBlank(annotations, "alertMessage", externAlert.getAlertMessage());
putIfNotBlank(annotations, "traceId", externAlert.getTraceId());
putIfNotBlank(annotations, "ruleUrl", externAlert.getRuleUrl());
putIfNotBlank(annotations, "entityUrl", externAlert.getEntityUrl());
putIfNotBlank(annotations, "alertRuleUrl", externAlert.getAlertRuleUrl());
putIfNotBlank(annotations, "alertHistoryUrl", externAlert.getAlertHistoryUrl());
return annotations;
}
private String getAlertContent(AlibabaCloudCmsExternAlert externAlert) {
if (StringUtils.isNotBlank(externAlert.getAlertMessage())) {
return externAlert.getAlertMessage();
}
if (StringUtils.isNotBlank(externAlert.getSubject())) {
return externAlert.getSubject();
}
return "Alibaba Cloud Monitor alert";
}
private String convertSeverity(String severity) {
if (StringUtils.isBlank(severity)) {
return null;
}
return switch (severity.toUpperCase(Locale.ROOT)) {
case "EMERGENCY" -> CommonConstants.ALERT_SEVERITY_EMERGENCY;
case "CRITICAL" -> CommonConstants.ALERT_SEVERITY_CRITICAL;
case "WARN", "WARNING" -> CommonConstants.ALERT_SEVERITY_WARNING;
case "INFO", "INFORMATIONAL" -> CommonConstants.ALERT_SEVERITY_INFO;
default -> severity.toLowerCase(Locale.ROOT);
};
}
private void putValues(Map<String, String> target, Map<String, Object> values) {
if (values == null || values.isEmpty()) {
return;
}
values.forEach((key, value) -> putValue(target, key, value));
}
private void putValue(Map<String, String> target, String key, Object value) {
if (StringUtils.isBlank(key) || value == null) {
return;
}
String stringValue;
if (value instanceof Map<?, ?> || value instanceof Collection<?>) {
stringValue = JsonUtil.toJson(value);
} else {
stringValue = String.valueOf(value);
}
putIfNotBlank(target, key, stringValue);
}
private void putIfNotBlank(Map<String, String> target, String key, String value) {
if (StringUtils.isNotBlank(value)) {
target.put(key, value);
}
}
}
@@ -0,0 +1,180 @@
/*
* 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;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import java.time.Instant;
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
import org.apache.hertzbeat.alert.service.impl.AlibabaCloudCmsExternAlertService;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
/**
* Unit test for {@link AlibabaCloudCmsExternAlertService}.
*/
@ExtendWith(MockitoExtension.class)
class AlibabaCloudCmsExternAlertServiceTest {
private static final long EVENT_TIME = 1785300000123L;
@Mock
private AlarmCommonReduce alarmCommonReduce;
private AlibabaCloudCmsExternAlertService externAlertService;
@BeforeEach
void setUp() {
externAlertService = new AlibabaCloudCmsExternAlertService(alarmCommonReduce);
}
@Test
void shouldConvertTriggeredAlert() {
externAlertService.addExternAlert("""
{
"specversion": "1.0",
"id": "alert-event-1",
"type": "ALERT",
"subtype": "NORMAL_TRIGGER",
"time": "2026-07-29T06:00:00Z",
"timestamp": 1785300000123,
"subject": "ECS CPU usage is high",
"severity": "WARNING",
"status": "OCCURRED",
"userId": "123456",
"ruleId": "rule-1",
"workspace": "default-cms-123456-cn-hangzhou",
"traceId": "trace-1",
"alertMessage": "CPU usage exceeded 80%",
"alertEntityId": "ecs:i-123",
"resource": {
"entity": {
"domain": "ecs",
"entity_type": "instance",
"entity_id": "i-123",
"prop": {
"instanceName": "api-server"
}
},
"tags": {
"regionId": "cn-hangzhou",
"environment": "production"
}
},
"labels": {
"_cms_region": "cn-hangzhou",
"customNumber": 7
},
"annotations": {
"current_value": "92.5"
},
"data": {
"value": 92.5,
"threshold": 80,
"comparisonOperator": ">"
},
"alertEntityFields": {
"privateIp": "10.0.0.1"
},
"alertHistoryUrl": "https://cmsnext.console.aliyun.com/history",
"futureField": "ignored"
}
""");
SingleAlert alert = captureAlert();
assertEquals(CommonConstants.ALERT_STATUS_FIRING, alert.getStatus());
assertEquals(EVENT_TIME, alert.getStartAt());
assertEquals(EVENT_TIME, alert.getActiveAt());
assertNull(alert.getEndAt());
assertEquals("CPU usage exceeded 80%", alert.getContent());
assertEquals("alibabacloud-cms", alert.getLabels().get("__source__"));
assertEquals("ECS CPU usage is high", alert.getLabels().get("alertname"));
assertEquals(CommonConstants.ALERT_SEVERITY_WARNING, alert.getLabels().get("severity"));
assertEquals("instance", alert.getLabels().get("resourceType"));
assertEquals("i-123", alert.getLabels().get("resourceId"));
assertEquals("7", alert.getLabels().get("customNumber"));
assertEquals("api-server", alert.getAnnotations().get("instanceName"));
assertEquals("92.5", alert.getAnnotations().get("value"));
assertEquals("80", alert.getAnnotations().get("threshold"));
assertEquals("10.0.0.1", alert.getAnnotations().get("privateIp"));
}
@Test
void shouldConvertResolvedAlertAndIsoTime() {
externAlertService.addExternAlert("""
{
"subtype": "NORMAL_RESOLVE",
"time": "2026-07-29T06:00:00Z",
"subject": "ECS CPU usage is high",
"severity": "CRITICAL",
"status": "RESOLVED",
"labels": {
"instanceId": "i-123"
}
}
""");
SingleAlert alert = captureAlert();
long expectedTime = Instant.parse("2026-07-29T06:00:00Z").toEpochMilli();
assertEquals(CommonConstants.ALERT_STATUS_RESOLVED, alert.getStatus());
assertEquals(expectedTime, alert.getStartAt());
assertNull(alert.getActiveAt());
assertEquals(expectedTime, alert.getEndAt());
assertEquals("ECS CPU usage is high", alert.getContent());
assertEquals(CommonConstants.ALERT_SEVERITY_CRITICAL, alert.getLabels().get("severity"));
}
@Test
void shouldTreatRecoveredStatusAsResolved() {
externAlertService.addExternAlert("""
{
"timestamp": 1785300000123,
"subject": "Recovered alert",
"status": "RECOVERED"
}
""");
SingleAlert alert = captureAlert();
assertEquals(CommonConstants.ALERT_STATUS_RESOLVED, alert.getStatus());
assertEquals(EVENT_TIME, alert.getEndAt());
}
@Test
void shouldIgnoreInvalidPayload() {
externAlertService.addExternAlert("invalid json");
externAlertService.addExternAlert("{\"subject\":\"missing status\"}");
verify(alarmCommonReduce, never()).reduceAndSendAlarm(any(SingleAlert.class));
assertEquals("alibabacloud-cms", externAlertService.supportSource());
}
private SingleAlert captureAlert() {
ArgumentCaptor<SingleAlert> captor = ArgumentCaptor.forClass(SingleAlert.class);
verify(alarmCommonReduce).reduceAndSendAlarm(captor.capture());
return captor.getValue();
}
}
@@ -80,6 +80,11 @@ export class AlertIntegrationComponent implements OnInit {
name: this.i18nSvc.fanyi('alert.integration.source.tencent'),
icon: 'assets/img/integration/tencent.svg'
},
{
id: 'alibabacloud-cms',
name: this.i18nSvc.fanyi('alert.integration.source.alibabacloud-cms'),
icon: 'assets/img/integration/alibabacloud.svg'
},
{
id: 'alibabacloud-sls',
name: this.i18nSvc.fanyi('alert.integration.source.alibabacloud-sls'),
@@ -0,0 +1,75 @@
> Send Alibaba Cloud Monitor 2.0 alerts to the HertzBeat alert platform through a webhook.
### Prepare a HertzBeat API token
1. Click **Manage API Tokens** in the upper-right corner of this page.
2. Create a token and save it securely. The complete token is displayed only once.
### Create an Alibaba Cloud Monitor webhook
1. Log on to the [Alibaba Cloud Monitor 2.0 console](https://cmsnext.console.aliyun.com/).
2. Select or create the target workspace, then go to **Alert Center** > **Notification Management** > **Notification Objects**.
3. Open the **Custom Webhook** tab and click **Create Webhook**.
4. Configure the webhook:
- Name: `HertzBeat`
- Identifier: for example, `hertzbeat`
- URL:
```text
http://{hertzbeat_host}:1157/api/alerts/report/alibabacloud-cms
```
- Headers: add `Authorization` with the value `Bearer {token}`
- Method: `POST`
- Data format: `JSON`
- Language: select as needed
5. Save the webhook.
> Use the notification objects in an Alibaba Cloud Monitor 2.0 workspace. Notification objects in Prometheus Monitoring or ARMS Alert Management use a different webhook format and are not supported by this integration.
> `{hertzbeat_host}` must be publicly reachable from Alibaba Cloud Monitor. Expose this endpoint through an HTTPS reverse proxy in production.
### Bind an alert rule
1. Go to **Alert Center** > **Alert Management** > **Alert Rules**.
2. Create or edit an alert rule.
3. Select the HertzBeat custom webhook in the alert notification settings.
4. Enable recovery notifications if alerts should be resolved automatically in HertzBeat.
5. Save and enable the alert rule.
### Field mapping
| Alibaba Cloud Monitor field | HertzBeat field |
| --- | --- |
| `status: OCCURRED/PERSISTENT` | `status: firing` |
| `status: RESOLVED/RECOVERED` | `status: resolved` |
| `subject` | `labels.alertname` |
| `severity` | `labels.severity` |
| `alertMessage` | Alert content |
| `labels`, `resource.tags` | Alert labels |
| `annotations`, threshold, and resource properties | Alert annotations |
| `timestamp` or `time` | Alert time |
### Troubleshooting
#### The webhook returns 401 or 403
- Make sure the HertzBeat API token is active.
- Make sure the header is named `Authorization` and its value starts with `Bearer `.
#### HertzBeat does not receive an alert
- Make sure the webhook URL and port are publicly reachable.
- Make sure the data format is `JSON` and the request method is `POST`.
- Check Alibaba Cloud Monitor alert history and the HertzBeat service logs.
- If source IP allowlisting is enabled, use the latest CIDR list in the Alibaba Cloud documentation.
#### An alert is not resolved automatically
- Make sure recovery notifications are enabled in the alert rule or notification policy.
- Make sure the recovery webhook contains a `status` of `RESOLVED` or `RECOVERED`.
For more information:
- [Alibaba Cloud Monitor notification objects and webhook fields](https://help.aliyun.com/en/cms/cloudmonitor-2-0/notification-object)
- [Alibaba Cloud Monitor alert rules](https://help.aliyun.com/en/cms/cloudmonitor-2-0/alert-rules-cms-2-0)
@@ -0,0 +1,75 @@
> 将阿里云云监控 2.0 的告警通过 Webhook 发送到 HertzBeat 告警平台。
### 准备 HertzBeat API Token
1. 单击页面右上角的 **管理 API Token**
2. 创建一个 Token 并立即妥善保存。Token 只会完整显示一次。
### 创建阿里云云监控 Webhook
1. 登录 [阿里云云监控 2.0 控制台](https://cmsnext.console.aliyun.com/)。
2. 选择或创建目标工作空间,然后进入 **告警中心** > **通知管理** > **通知对象**
3. 选择 **自定义 Webhook** 页签,单击 **新建 Webhook**
4. 填写 Webhook 配置:
- 名称:`HertzBeat`
- 标识符:例如 `hertzbeat`
- URL
```text
http://{hertzbeat_host}:1157/api/alerts/report/alibabacloud-cms
```
- Headers:添加 `Authorization`,值为 `Bearer {token}`
- Method`POST`
- 数据格式:`JSON`
- 语言:按需选择
5. 保存 Webhook。
> 请使用云监控 2.0 工作空间内的通知对象。Prometheus 监控或 ARMS 告警管理中的通知对象使用不同的 Webhook 格式,不适用于此集成。
> `{hertzbeat_host}` 必须是阿里云云监控可以访问的公网地址。生产环境建议通过 HTTPS 反向代理暴露此接口。
### 绑定告警规则
1. 进入 **告警中心** > **告警管理** > **告警规则**。
2. 创建或编辑告警规则。
3. 在告警通知中选择上一步创建的 HertzBeat 自定义 Webhook。
4. 如需在 HertzBeat 中自动恢复告警,请同时启用恢复通知。
5. 保存并启用告警规则。
### 字段映射
| 阿里云云监控字段 | HertzBeat 字段 |
| --- | --- |
| `status: OCCURRED/PERSISTENT` | `status: firing` |
| `status: RESOLVED/RECOVERED` | `status: resolved` |
| `subject` | `labels.alertname` |
| `severity` | `labels.severity` |
| `alertMessage` | 告警内容 |
| `labels`、`resource.tags` | 告警标签 |
| `annotations`、阈值和资源属性 | 告警注解 |
| `timestamp` 或 `time` | 告警时间 |
### 常见问题
#### 返回 401 或 403
- 确认已创建有效的 HertzBeat API Token。
- 确认 Webhook Header 名称为 `Authorization`,值以 `Bearer ` 开头。
#### HertzBeat 未收到告警
- 确认 Webhook URL 可从公网访问,并且端口已放行。
- 确认数据格式选择为 `JSON`,请求方法选择为 `POST`。
- 检查阿里云云监控的告警历史以及 HertzBeat 服务日志。
- 若配置了来源 IP 白名单,请以阿里云官方文档中的最新地址段为准。
#### 告警没有自动恢复
- 确认告警规则或通知策略已启用恢复通知。
- 确认恢复 Webhook 中的 `status` 为 `RESOLVED` 或 `RECOVERED`。
更多信息请参考:
- [阿里云云监控通知对象与 Webhook 字段](https://help.aliyun.com/zh/cms/cloudmonitor-2-0/notification-object)
- [阿里云云监控告警规则](https://help.aliyun.com/zh/cms/cloudmonitor-2-0/alert-rules-cms-2-0)
+1
View File
@@ -100,6 +100,7 @@
"alert.integration.source.skywalking": "SkyWalking",
"alert.integration.source.uptime-kuma": "Uptime Kuma",
"alert.integration.source.zabbix": "Zabbix",
"alert.integration.source.alibabacloud-cms": "Alibaba Cloud Monitor",
"alert.integration.source.alibabacloud-sls": "AlibabaCloud-SLS",
"alert.integration.source.huaweicloud-ces": "Huawei Cloud Eye",
"alert.integration.source.volcengine": "Volcengine Monitoring",
+1
View File
@@ -100,6 +100,7 @@
"alert.integration.source.skywalking": "SkyWalking",
"alert.integration.source.uptime-kuma": "Uptime Kuma",
"alert.integration.source.zabbix": "Zabbix",
"alert.integration.source.alibabacloud-cms": "Alibaba Cloud Monitor",
"alert.integration.source.alibabacloud-sls": "AlibabaCloud-SLS",
"alert.integration.source.huaweicloud-ces": "Huawei Cloud Eye",
"alert.integration.source.volcengine": "火山エンジン監視",
+1
View File
@@ -100,6 +100,7 @@
"alert.integration.source.skywalking": "SkyWalking",
"alert.integration.source.uptime-kuma": "Uptime Kuma",
"alert.integration.source.zabbix": "Zabbix",
"alert.integration.source.alibabacloud-cms": "Alibaba Cloud Monitor",
"alert.integration.source.alibabacloud-sls": "AlibabaCloud-SLS",
"alert.integration.source.huaweicloud-ces": "Huawei Cloud Eye",
"alert.integration.source.volcengine": "Volcengine Monitoring",
+1
View File
@@ -251,6 +251,7 @@
"alert.integration.source.prometheus": "Prometheus",
"alert.integration.source.tencent": "Monitoramento de nuvem Tencent",
"alert.integration.source.webhook": "PadrãoWebhook",
"alert.integration.source.alibabacloud-cms": "Alibaba Cloud Monitor",
"alert.integration.source.alibabacloud-sls": "AlibabaCloud-SLS",
"alert.integration.source.huaweicloud-ces": "Huawei Cloud Eye",
"alert.integration.source.volcengine": "Volcengine",
+1
View File
@@ -100,6 +100,7 @@
"alert.integration.source.skywalking": "SkyWalking",
"alert.integration.source.uptime-kuma": "Uptime Kuma",
"alert.integration.source.zabbix": "Zabbix",
"alert.integration.source.alibabacloud-cms": "阿里云云监控",
"alert.integration.source.alibabacloud-sls": "阿里云日志服务 SLS",
"alert.integration.source.huaweicloud-ces": "华为云监控服务",
"alert.integration.source.volcengine": "火山引擎云监控",
+1
View File
@@ -100,6 +100,7 @@
"alert.integration.source.skywalking": "SkyWalking",
"alert.integration.source.uptime-kuma": "Uptime Kuma",
"alert.integration.source.zabbix": "Zabbix",
"alert.integration.source.alibabacloud-cms": "阿里雲雲監控",
"alert.integration.source.alibabacloud-sls": "阿里雲端日誌服務 SLS",
"alert.integration.source.huaweicloud-ces": "華為雲監控服務",
"alert.integration.source.volcengine": "火山引擎監控",