diff --git a/sermant-backend/pom.xml b/sermant-backend/pom.xml
index 5be25abe96..3678d9f7e2 100644
--- a/sermant-backend/pom.xml
+++ b/sermant-backend/pom.xml
@@ -18,6 +18,17 @@
4.1.75.Final
2.5.8
1.12.1
+ 3.9.1
+ 1.18.12
+ 1.2.83
+ 2.6
+ 4.13.1
+ 5.6.2
+ 2.28.2
+ 1.7.32
+ 3.9
+ 4.3.1
+ 2.0.9
${project.basedir}/src/main/webapp/frontend
@@ -85,50 +96,73 @@
com.google.protobuf
protobuf-java
- 3.9.1
+ ${protobuf-java.version}
org.projectlombok
lombok
- 1.18.12
+ ${lombok.version}
com.alibaba
fastjson
- 1.2.83
+ ${fastjson.version}
commons-lang
commons-lang
- 2.6
+ ${commons-lang.version}
junit
junit
- 4.13.1
+ ${junit.version}
test
org.junit.jupiter
junit-jupiter
- 5.6.2
+ ${junit-jupiter.version}
test
org.mockito
mockito-core
- 2.28.2
+ ${mockito-core.version}
test
org.slf4j
slf4j-api
- 1.7.32
+ ${slf4j-api.version}
org.apache.commons
commons-lang3
- 3.9
+ ${commons-lang3.version}
+
+
+ redis.clients
+ jedis
+ ${jedis.version}
+
+
+ org.powermock
+ powermock-core
+ ${powermock.version}
+ test
+
+
+ org.powermock
+ powermock-api-mockito2
+ ${powermock.version}
+ test
+
+
+ org.powermock
+ powermock-module-junit4
+ ${powermock.version}
+ test
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/Backend.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/Backend.java
index 30ae0d74ad..007928326f 100644
--- a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/Backend.java
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/Backend.java
@@ -19,6 +19,7 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
+import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 启动类
@@ -29,6 +30,7 @@
*/
@SpringBootApplication
@ConfigurationPropertiesScan
+@EnableScheduling
public class Backend {
public static void main(String[] args) {
SpringApplication.run(Backend.class, args);
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/BackendConfig.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/BackendConfig.java
new file mode 100644
index 0000000000..be0bb9e6f2
--- /dev/null
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/BackendConfig.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed 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 com.huaweicloud.sermant.backend.common.conf;
+
+import com.huaweicloud.sermant.backend.dao.DatabaseType;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+import java.util.Locale;
+
+/**
+ * 事件配置
+ *
+ * @author xuezechao
+ * @since 2023-03-02
+ */
+@Component
+@Configuration
+public class BackendConfig {
+
+ /**
+ * 数据库类型
+ */
+ @Value("${database.type}")
+ private String database;
+
+ /**
+ * 数据库地址
+ */
+ @Value("${database.address}")
+ private String url;
+
+ @Value("${database.user}")
+ private String user;
+
+ @Value("${database.password}")
+ private String password;
+
+ @Value("${database.expire}")
+ private String expire;
+
+ @Value("${webhook.eventpush.level}")
+ private String webhookPushEventThreshold;
+
+ @Value("${database.version}")
+ private String version;
+
+ @Value("${database.max.total}")
+ private String maxTotal;
+
+ @Value("${database.max.idle}")
+ private String maxIdle;
+
+ @Value("${database.timeout}")
+ private String timeout;
+
+ @Value("${session.expire}")
+ private String sessionTimeout;
+
+ @Value("${database.filter.thread.num}")
+ private String filterThreadNum;
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public DatabaseType getDatabase() {
+ return DatabaseType.valueOf(database.toUpperCase(Locale.ROOT));
+ }
+
+ public void setDatabase(String db) {
+ this.database = db;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public String getExpire() {
+ return expire;
+ }
+
+ public void setExpire(String expire) {
+ this.expire = expire;
+ }
+
+ public void setWebhookPushEventThreshold(String webhookPushEventThreshold) {
+ this.webhookPushEventThreshold = webhookPushEventThreshold;
+ }
+
+ public String getWebhookPushEventThreshold() {
+ return webhookPushEventThreshold;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getMaxIdle() {
+ return maxIdle;
+ }
+
+ public void setMaxIdle(String maxIdle) {
+ this.maxIdle = maxIdle;
+ }
+
+ public String getMaxTotal() {
+ return maxTotal;
+ }
+
+ public void setMaxTotal(String maxTotal) {
+ this.maxTotal = maxTotal;
+ }
+
+ public String getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(String timeout) {
+ this.timeout = timeout;
+ }
+
+ public String getSessionTimeout() {
+ return sessionTimeout;
+ }
+
+ public void setSessionTimeout(String sessionTimeout) {
+ this.sessionTimeout = sessionTimeout;
+ }
+
+ public String getFilterThreadNum() {
+ return filterThreadNum;
+ }
+
+ public void setFilterThreadNum(String filterThreadNum) {
+ this.filterThreadNum = filterThreadNum;
+ }
+}
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/CommonConst.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/CommonConst.java
new file mode 100644
index 0000000000..ecfd5dd71b
--- /dev/null
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/common/conf/CommonConst.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed 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 com.huaweicloud.sermant.backend.common.conf;
+
+/**
+ * 公共配置常量
+ *
+ * @author xuezechao
+ * @since 2023-03-02
+ */
+public class CommonConst {
+
+ /**
+ * 钉钉webhook名称
+ */
+ public static final String DINGDING_WEBHOOK_NAME = "DingDing";
+
+ /**
+ * 钉钉webhook id
+ */
+ public static final int DINGDING_WEBHOOK_ID = 1;
+
+ /**
+ * 飞书webhook名称
+ */
+ public static final String FEISHU_WEBHOOK_NAME = "Feishu";
+
+ /**
+ * 飞书webhook id
+ */
+ public static final int FEISHU_WEBHOOK_ID = 0;
+
+ /**
+ * welink WEBHOOK名称
+ */
+ public static final String WELINK_WEBHOOK_NAME = "Welink";
+
+ /**
+ * welink WEBHOOK id
+ */
+ public static final int WELINK_WEBHOOK_ID = 2;
+
+ /**
+ * 默认redis地址
+ */
+ public static final String DEFAULT_REDIS_ADDRESS = "127.0.0.1";
+
+ /**
+ * 默认redis端口
+ */
+ public static final int DEFAULT_REDIS_PORT = 6379;
+
+ /**
+ * redis 实例元数据key
+ */
+ public static final String REDIS_HASH_KEY_OF_INSTANCE_META = "sermant_meta";
+
+ /**
+ * redis 事件key
+ */
+ public static final String REDIS_EVENT_KEY = "sermant_events_hash";
+
+ /**
+ * redis 事件field集合的key
+ */
+ public static final String REDIS_EVENT_FIELD_SET_KEY = "sermant_event_keyset";
+
+ /**
+ * redis 全匹配字符*
+ */
+ public static final String FULL_MATCH_KEY = ".*";
+
+ /**
+ * 拼接redis key 字符
+ */
+ public static final String JOIN_REDIS_KEY = "_";
+
+ /**
+ * redis 地址切分字符
+ */
+ public static final String REDIS_ADDRESS_SPLIT = ":";
+
+ /**
+ * 前端页面事件每页展示默认数量
+ */
+ public static final int DEFAULT_PAGE_SIZE = 10;
+
+ private CommonConst() {
+
+ }
+}
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/DatabaseType.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/DatabaseType.java
new file mode 100644
index 0000000000..4b6cc18a59
--- /dev/null
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/DatabaseType.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed 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 com.huaweicloud.sermant.backend.dao;
+
+/**
+ * 数据库类型
+ *
+ * @since 2023-03-02
+ * @author xuezechao
+ */
+public enum DatabaseType {
+
+ /**
+ * redis 数据库
+ */
+ REDIS,
+}
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/EventDao.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/EventDao.java
new file mode 100644
index 0000000000..5292ff99b2
--- /dev/null
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/EventDao.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed 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 com.huaweicloud.sermant.backend.dao;
+
+import com.huaweicloud.sermant.backend.entity.InstanceMeta;
+import com.huaweicloud.sermant.backend.entity.event.Event;
+import com.huaweicloud.sermant.backend.entity.event.EventsRequestEntity;
+import com.huaweicloud.sermant.backend.entity.event.QueryCacheSizeEntity;
+import com.huaweicloud.sermant.backend.entity.event.QueryResultEventInfoEntity;
+
+import java.util.List;
+
+/**
+ * 数据库接口
+ *
+ * @author xuezechao
+ * @since 2023-03-02
+ */
+public interface EventDao {
+
+ /**
+ * 增加事件
+ *
+ * @param event 事件
+ * @return true/false
+ */
+ boolean addEvent(Event event);
+
+ /**
+ * 增加agent实例
+ *
+ * @param agentInstanceMeta agent 实例
+ * @return true/false
+ */
+ boolean addInstanceMeta(InstanceMeta agentInstanceMeta);
+
+ /**
+ * 事件查询
+ *
+ * @param eventsRequestEntity 查询条件
+ * @return 查询结果
+ */
+ List queryEvent(EventsRequestEntity eventsRequestEntity);
+
+ /**
+ * 查询某页数据
+ *
+ * @param sessionId id
+ * @param page 页码
+ * @return 查询结果
+ */
+ List queryEventPage(String sessionId, int page);
+
+ /**
+ * 获取查询结果数据量
+ *
+ * @param eventsRequestEntity 查询参数
+ * @return 查询结果数据量
+ */
+ QueryCacheSizeEntity getQueryCacheSize(EventsRequestEntity eventsRequestEntity);
+
+ /**
+ * 清理过期事件
+ */
+ void cleanOverDueEventTimerTask();
+}
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedis.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedis.java
new file mode 100644
index 0000000000..decb2dade3
--- /dev/null
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedis.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed 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 com.huaweicloud.sermant.backend.dao.redis;
+
+import com.huaweicloud.sermant.backend.common.conf.BackendConfig;
+import com.huaweicloud.sermant.backend.dao.EventDao;
+import com.huaweicloud.sermant.backend.entity.InstanceMeta;
+import com.huaweicloud.sermant.backend.entity.event.Event;
+import com.huaweicloud.sermant.backend.entity.event.EventsRequestEntity;
+import com.huaweicloud.sermant.backend.entity.event.QueryCacheSizeEntity;
+import com.huaweicloud.sermant.backend.entity.event.QueryResultEventInfoEntity;
+
+import redis.clients.jedis.exceptions.JedisConnectionException;
+import redis.clients.jedis.exceptions.JedisDataException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * redis数据库数据处理
+ *
+ * @author xuezechao
+ * @since 2023-03-02
+ */
+@Component
+@EnableScheduling
+public class EventDaoForRedis implements EventDao {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EventDaoForRedis.class);
+
+ private EventDao jedis;
+
+ /**
+ * 构造函数
+ *
+ * @param backendConfig 配置
+ */
+ public EventDaoForRedis(BackendConfig backendConfig) {
+ try {
+ List redisAddress = Arrays.asList(backendConfig.getUrl().split(";"));
+ if (redisAddress.size() == 1) {
+ jedis = new RedisClientImpl(backendConfig);
+ } else if (redisAddress.size() > 1) {
+ jedis = new RedisClusterClientImpl(backendConfig);
+ }
+ } catch (JedisConnectionException | JedisDataException e) {
+ LOGGER.error(String.format(Locale.ROOT, "connect redis failed, error message: [%s]", e.getMessage()));
+ }
+ }
+
+ /**
+ * 插入事件
+ *
+ * @param event 事件
+ * @return true/false
+ */
+ @Override
+ public boolean addEvent(Event event) {
+ return jedis.addEvent(event);
+ }
+
+ /**
+ * 插入agent实例
+ *
+ * @param instanceMeta agent元数据
+ * @return true/false
+ */
+ @Override
+ public boolean addInstanceMeta(InstanceMeta instanceMeta) {
+ return jedis.addInstanceMeta(instanceMeta);
+ }
+
+ /**
+ * 事件查询
+ *
+ * @param eventsRequestEntity 查询条件
+ * @return 查询结果
+ */
+ @Override
+ public List queryEvent(EventsRequestEntity eventsRequestEntity) {
+ return jedis.queryEvent(eventsRequestEntity);
+ }
+
+ /**
+ * 获取某一页数据
+ *
+ * @param page 页码
+ * @return 查询数据
+ */
+ @Override
+ public List queryEventPage(String sessionId, int page) {
+ return jedis.queryEventPage(sessionId, page);
+ }
+
+ /**
+ * 获取查询数据类型数量
+ *
+ * @return 查询结果数量
+ */
+ @Override
+ public QueryCacheSizeEntity getQueryCacheSize(EventsRequestEntity eventsRequestEntity) {
+ return jedis.getQueryCacheSize(eventsRequestEntity);
+ }
+
+ @Override
+ @Scheduled(fixedDelayString = "${database.fixedDelay}")
+ public void cleanOverDueEventTimerTask() {
+ jedis.cleanOverDueEventTimerTask();
+ }
+}
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClientImpl.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClientImpl.java
new file mode 100644
index 0000000000..79b4503217
--- /dev/null
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClientImpl.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed 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 com.huaweicloud.sermant.backend.dao.redis;
+
+import com.huaweicloud.sermant.backend.common.conf.CommonConst;
+import com.huaweicloud.sermant.backend.common.conf.BackendConfig;
+import com.huaweicloud.sermant.backend.dao.EventDao;
+import com.huaweicloud.sermant.backend.entity.InstanceMeta;
+import com.huaweicloud.sermant.backend.entity.event.Event;
+import com.huaweicloud.sermant.backend.entity.event.EventLevel;
+import com.huaweicloud.sermant.backend.entity.event.EventType;
+import com.huaweicloud.sermant.backend.entity.event.EventsRequestEntity;
+import com.huaweicloud.sermant.backend.entity.event.QueryCacheSizeEntity;
+import com.huaweicloud.sermant.backend.entity.event.QueryResultEventInfoEntity;
+import com.huaweicloud.sermant.backend.util.StringUtils;
+
+import com.alibaba.fastjson.JSONObject;
+
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+import redis.clients.jedis.params.ScanParams;
+import redis.clients.jedis.resps.ScanResult;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.stream.Collectors;
+
+/**
+ * redis单机客户端
+ *
+ * @author xuezechao
+ * @since 2023-03-02
+ */
+public class RedisClientImpl implements EventDao {
+
+ private static final int EVENT_LEVEL_INDEX = 3;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(RedisClientImpl.class);
+
+ private JedisPool jedisPool;
+
+ private BackendConfig backendConfig;
+
+ /**
+ * 构造redis 连接池
+ *
+ * @param backendConfig 配置
+ */
+ public RedisClientImpl(BackendConfig backendConfig) {
+ this.backendConfig = backendConfig;
+ JedisPoolConfig config = new JedisPoolConfig();
+ config.setMaxTotal(Integer.parseInt(backendConfig.getMaxTotal()));
+ config.setMaxIdle(Integer.parseInt(backendConfig.getMaxIdle()));
+ if (backendConfig.getVersion().compareTo("6.0") < 0) {
+ jedisPool = new JedisPool(
+ config,
+ Arrays.asList(backendConfig.getUrl().split(CommonConst.REDIS_ADDRESS_SPLIT)).get(0),
+ Integer.parseInt(Arrays.asList(backendConfig.getUrl().split(CommonConst.REDIS_ADDRESS_SPLIT)).get(1)),
+ Integer.parseInt(backendConfig.getTimeout()),
+ backendConfig.getPassword());
+ } else {
+ jedisPool = new JedisPool(
+ config,
+ Arrays.asList(backendConfig.getUrl().split(CommonConst.REDIS_ADDRESS_SPLIT)).get(0),
+ Integer.parseInt(Arrays.asList(backendConfig.getUrl().split(CommonConst.REDIS_ADDRESS_SPLIT)).get(1)),
+ Integer.parseInt(backendConfig.getTimeout()),
+ backendConfig.getUser(),
+ backendConfig.getPassword());
+ }
+ }
+
+ @Override
+ public boolean addEvent(Event event) {
+ try (Jedis jedis = jedisPool.getResource()) {
+ String instanceMeta = jedis.hget(CommonConst.REDIS_HASH_KEY_OF_INSTANCE_META, event.getMetaHash());
+ if (StringUtils.isEmpty(instanceMeta)) {
+ LOGGER.error("add event failed, event:{}, error message:[instance not exist]", event);
+ return false;
+ }
+ InstanceMeta agentInstanceMeta = JSONObject.parseObject(instanceMeta, InstanceMeta.class);
+
+ // 获取事件字段
+ String field = getEventField(agentInstanceMeta, event);
+
+ // 检查是否有相同field
+ field = field + CommonConst.JOIN_REDIS_KEY + getSameFieldNum(field);
+
+ // 写入事件
+ jedis.hset(CommonConst.REDIS_EVENT_KEY, field, getEventStr(event, agentInstanceMeta));
+
+ // 写入类型索引
+ jedis.zadd(CommonConst.REDIS_EVENT_FIELD_SET_KEY, event.getTime(), field);
+ if (event.getEventLevel().equals(EventLevel.EMERGENCY)) {
+ jedis.incrBy(EventLevel.EMERGENCY.toString(), 1);
+ } else if (event.getEventLevel().equals(EventLevel.IMPORTANT)) {
+ jedis.incrBy(EventLevel.IMPORTANT.toString(), 1);
+ } else {
+ jedis.incrBy(EventLevel.NORMAL.toString(), 1);
+ }
+ return true;
+ } catch (IllegalStateException e) {
+ LOGGER.error("add event failed, event:{}, error message:{}", event, e.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * 事件序列化
+ *
+ * @param event 事件
+ * @param agentInstanceMeta 事件归属的实例
+ * @return 事件字符串
+ */
+ private String getEventStr(Event event, InstanceMeta agentInstanceMeta) {
+ QueryResultEventInfoEntity queryResultEventInfoEntity = new QueryResultEventInfoEntity();
+ queryResultEventInfoEntity.setTime(event.getTime());
+ queryResultEventInfoEntity.setScope(event.getScope());
+ queryResultEventInfoEntity.setLevel(event.getEventLevel().toString().toUpperCase(Locale.ROOT));
+ queryResultEventInfoEntity.setType(event.getEventType().getDescription());
+ if (event.getEventType().getDescription().equals(EventType.LOG.getDescription())) {
+ queryResultEventInfoEntity.setInfo(event.getLogInfo());
+ } else {
+ queryResultEventInfoEntity.setInfo(event.getEventInfo());
+ }
+ HashMap meta = new HashMap<>();
+ meta.put("service", agentInstanceMeta.getService());
+ meta.put("ip", agentInstanceMeta.getNode().getIp());
+ queryResultEventInfoEntity.setMeta(meta);
+ return JSONObject.toJSONString(queryResultEventInfoEntity);
+ }
+
+ @Override
+ public boolean addInstanceMeta(InstanceMeta instanceMeta) {
+ try (Jedis jedis = jedisPool.getResource()) {
+ String meta = JSONObject.toJSONString(instanceMeta);
+ if (StringUtils.isEmpty(
+ jedis.hget(CommonConst.REDIS_HASH_KEY_OF_INSTANCE_META,
+ instanceMeta.getMetaHash()))) {
+ // 写入实例信息
+ jedis.hset(CommonConst.REDIS_HASH_KEY_OF_INSTANCE_META, instanceMeta.getMetaHash(), meta);
+ }
+ return true;
+ } catch (IllegalStateException e) {
+ LOGGER.error("add instance meta failed, instance meta:{}, error message:{}", instanceMeta, e.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * 获取相同field 数量
+ *
+ * @param field field
+ * @return 相同field 数量
+ */
+ private int getSameFieldNum(String field) {
+ int result = 0;
+ try (Jedis jedis = jedisPool.getResource()) {
+ ScanResult> firstScanResult = jedis.hscan(
+ CommonConst.REDIS_EVENT_KEY,
+ String.valueOf(0),
+ new ScanParams().match(field + "*"));
+ int cursor = Integer.parseInt(firstScanResult.getCursor());
+ result += firstScanResult.getResult().size();
+ while (cursor > 0) {
+ ScanResult> scanResult = jedis.hscan(
+ CommonConst.REDIS_EVENT_KEY,
+ String.valueOf(cursor),
+ new ScanParams().match(field + "*"));
+ cursor = Integer.parseInt(scanResult.getCursor());
+ result += scanResult.getResult().size();
+ }
+ return result;
+ } catch (IllegalStateException e) {
+ LOGGER.error( "query same field failed, field:{}, error message:{}", field, e.getMessage());
+ return result;
+ }
+ }
+
+ @Override
+ public List queryEvent(EventsRequestEntity eventsRequestEntity) {
+ String pattern = getPattern(eventsRequestEntity);
+ try (Jedis jedis = jedisPool.getResource()) {
+ List queryResultByTime = queryByTimeRange(CommonConst.REDIS_EVENT_FIELD_SET_KEY,
+ eventsRequestEntity.getStartTime(),
+ eventsRequestEntity.getEndTime());
+ Collections.reverse(queryResultByTime);
+ queryResultByTime = filterQueryResult(queryResultByTime, pattern);
+ jedis.setex(
+ eventsRequestEntity.getSessionId(),
+ Integer.parseInt(backendConfig.getSessionTimeout()),
+ JSONObject.toJSONString(queryResultByTime));
+ return queryEventPage(eventsRequestEntity.getSessionId(), 1);
+ } catch (IllegalStateException e) {
+ LOGGER.error("query event failed, error message:{}", e.getMessage());
+ return new ArrayList<>();
+ }
+ }
+
+ /**
+ * 过滤查询结果
+ *
+ * @param queryResultByTime 按时间查询结果
+ * @param pattern 过滤规则
+ * @return 过滤结果
+ */
+ private List filterQueryResult(List queryResultByTime, String pattern) {
+ List fanList = new ArrayList<>();
+ if (queryResultByTime.size() <= 0) {
+ return fanList;
+ }
+ int threadSize = Integer.parseInt(backendConfig.getFilterThreadNum());
+ int dataSize = queryResultByTime.size();
+ int threadNum = 0;
+ if (dataSize % threadSize == 0) {
+ threadNum = dataSize / threadSize;
+ } else {
+ threadNum = dataSize / threadSize + 1;
+ }
+ ExecutorService exc = Executors.newFixedThreadPool(threadNum);
+ List>> tasks = new ArrayList<>();
+ Callable> task = null;
+ List cutList = null;
+ for (int i = 0; i < threadNum; i++) {
+
+ // 切割list
+ if (i == threadNum - 1) {
+ cutList = queryResultByTime.subList(i * threadSize, queryResultByTime.size());
+ } else {
+ cutList = queryResultByTime.subList(i * threadSize, (i + 1) * threadSize);
+ }
+
+ final List finalCutList = cutList;
+ task = new Callable>() {
+ @Override
+ public List call() throws Exception {
+ List newList = new ArrayList<>();
+ for (String a : finalCutList) {
+ if (a.matches(pattern)) {
+ newList.add(a);
+ }
+ }
+ return newList;
+ }
+ };
+ tasks.add(task);
+ }
+
+ try {
+ List>> results = exc.invokeAll(tasks);
+ for (Future> result : results) {
+ fanList.addAll(result.get());
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ LOGGER.error("filter query result failed, error message:{}", e.getMessage());
+ }
+ exc.shutdown();
+ return fanList;
+ }
+
+ /**
+ * 按时间范围查询事件
+ *
+ * @param key 集合key
+ * @param startTime 开始时间
+ * @param endTime 结束时间
+ * @return 查询结果
+ */
+ public List queryByTimeRange(String key, long startTime, long endTime) {
+ try (Jedis jedis = jedisPool.getResource()) {
+ return jedis.zrangeByScore(key, startTime, endTime);
+ } catch (IllegalStateException e) {
+ LOGGER.error("query event by time failed, key:{}, startTime:{}, endTime:{}, error message:{}",
+ key, startTime, endTime, e.getMessage());
+ return Collections.emptyList();
+ }
+ }
+
+ @Override
+ public List queryEventPage(String sessionId, int page) {
+ List result = new ArrayList<>();
+ try (Jedis jedis = jedisPool.getResource()) {
+ String events = jedis.get(sessionId);
+ if (!StringUtils.isEmpty(events)) {
+ List keyList = JSONObject.parseArray(events, String.class);
+ int startIndex = (page - 1) * CommonConst.DEFAULT_PAGE_SIZE;
+ int endIndex = Math.min(startIndex + CommonConst.DEFAULT_PAGE_SIZE, keyList.size());
+ for (String key : keyList.subList(startIndex, endIndex)) {
+ result.add(JSONObject.parseObject(
+ jedis.hget(CommonConst.REDIS_EVENT_KEY, key),
+ QueryResultEventInfoEntity.class));
+ }
+ }
+ return result;
+ } catch (IllegalStateException e) {
+ LOGGER.error("query event by page failed, sessionId:{}, error message:{}",
+ sessionId, e.getMessage());
+ return result;
+ }
+ }
+
+ @Override
+ public QueryCacheSizeEntity getQueryCacheSize(EventsRequestEntity eventsRequestEntity) {
+ QueryCacheSizeEntity queryCacheSize = new QueryCacheSizeEntity();
+ try (Jedis jedis = jedisPool.getResource()) {
+ queryCacheSize.setEmergencyNum(StringUtils.filterStr(jedis.get(EventLevel.EMERGENCY.toString())));
+ queryCacheSize.setImportantNum(StringUtils.filterStr(jedis.get(EventLevel.IMPORTANT.toString())));
+ queryCacheSize.setNormalNum(StringUtils.filterStr(jedis.get(EventLevel.NORMAL.toString())));
+ queryCacheSize.setTotal(queryCacheSize.getEmergencyNum()
+ + queryCacheSize.getImportantNum() + queryCacheSize.getNormalNum());
+ return queryCacheSize;
+ } catch (IllegalStateException e) {
+ LOGGER.error("query event size failed, sessionId:{}, error message:{}",
+ eventsRequestEntity.getSessionId(), e.getMessage());
+ return queryCacheSize;
+ }
+ }
+
+ /**
+ * 获取事件field
+ *
+ * @param agentInstanceMeta agent实例
+ * @param event 事件
+ * @return 事件对应field
+ */
+ public String getEventField(InstanceMeta agentInstanceMeta, Event event) {
+ String field = String.join(CommonConst.JOIN_REDIS_KEY,
+ getField(agentInstanceMeta.getService()),
+ getField(agentInstanceMeta.getNode().getIp()),
+ getField(String.valueOf(event.getEventType().getDescription()).toLowerCase(Locale.ROOT)),
+ getField(event.getEventLevel().toString().toLowerCase(Locale.ROOT)),
+ getField(event.getScope()),
+ getField(agentInstanceMeta.getInstanceId()),
+ agentInstanceMeta.getCluster() != null ? getField(agentInstanceMeta.getCluster().getCluster()) : "",
+ agentInstanceMeta.getEnvironment() != null ? getField(agentInstanceMeta.getEnvironment().getEnv()) : "",
+ getField(agentInstanceMeta.getAz()),
+ getField(event.getMetaHash()),
+ getField(String.valueOf(event.getTime())));
+ return field;
+ }
+
+ private String getField(String str) {
+ return !StringUtils.isEmpty(str) ? str : "";
+ }
+
+ /**
+ * 拼接查询条件
+ *
+ * @param event 查询条件
+ * @return 事件查询模版
+ */
+ private String getPattern(EventsRequestEntity event) {
+ List patterns = new ArrayList<>();
+ patterns.add(getListPattern(event.getService()));
+ patterns.add(getListPattern(event.getIp()));
+ patterns.add(getListPattern(event.getType() == null ? new ArrayList<>() : event.getType()));
+ patterns.add(getListPattern(event.getLevel() == null ? new ArrayList<>() : event.getLevel()));
+ patterns.add(getListPattern(event.getScope()));
+ patterns.add(CommonConst.FULL_MATCH_KEY);
+ return patterns.stream().map(String::valueOf).collect(Collectors.joining(CommonConst.JOIN_REDIS_KEY));
+ }
+
+ private String getListPattern(List strings) {
+ return strings.size() == 0 ? CommonConst.FULL_MATCH_KEY : "(" + String.join("|", strings) + ")";
+ }
+
+ /**
+ * 定时任务,清理过期数据
+ */
+ public void cleanOverDueEventTimerTask() {
+ Calendar calendar = Calendar.getInstance();
+ calendar.add(Calendar.SECOND, -Integer.parseInt(backendConfig.getExpire()));
+ List needCleanEvent = queryByTimeRange(
+ CommonConst.REDIS_EVENT_FIELD_SET_KEY, 0, calendar.getTimeInMillis());
+ try (Jedis jedis = jedisPool.getResource()) {
+ jedis.hdel(CommonConst.REDIS_EVENT_KEY, needCleanEvent.toArray(new String[0]));
+ jedis.zrem(CommonConst.REDIS_EVENT_FIELD_SET_KEY, needCleanEvent.toArray(new String[0]));
+ cleanOverDueEventLevel(jedis, needCleanEvent);
+ } catch (IllegalStateException e) {
+ LOGGER.error("delete over dur event failed, error message:{}", e.getMessage());
+ }
+ }
+
+ /**
+ * 删除过期事件同步设置事件级别数量
+ *
+ * @param jedis redis client
+ * @param needCleanEvent 需要删除的事件key
+ */
+ private void cleanOverDueEventLevel(Jedis jedis, List needCleanEvent) {
+ for (String key : needCleanEvent) {
+ EventLevel level = EventLevel.valueOf(
+ key.split(CommonConst.JOIN_REDIS_KEY)[EVENT_LEVEL_INDEX].toUpperCase(Locale.ROOT));
+ switch (level) {
+ case EMERGENCY:
+ jedis.decrBy(EventLevel.EMERGENCY.toString(), -1);
+ break;
+ case IMPORTANT:
+ jedis.decrBy(EventLevel.IMPORTANT.toString(), -1);
+ break;
+ case NORMAL:
+ jedis.decrBy(EventLevel.NORMAL.toString(), -1);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClusterClientImpl.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClusterClientImpl.java
new file mode 100644
index 0000000000..1f3bc9ce50
--- /dev/null
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/dao/redis/RedisClusterClientImpl.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed 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 com.huaweicloud.sermant.backend.dao.redis;
+
+import com.huaweicloud.sermant.backend.common.conf.BackendConfig;
+import com.huaweicloud.sermant.backend.dao.EventDao;
+import com.huaweicloud.sermant.backend.entity.InstanceMeta;
+import com.huaweicloud.sermant.backend.entity.event.Event;
+import com.huaweicloud.sermant.backend.entity.event.EventsRequestEntity;
+import com.huaweicloud.sermant.backend.entity.event.QueryCacheSizeEntity;
+import com.huaweicloud.sermant.backend.entity.event.QueryResultEventInfoEntity;
+
+import redis.clients.jedis.HostAndPort;
+import redis.clients.jedis.JedisCluster;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * redis集群客户端
+ *
+ * @author xuezechao
+ * @since 2023-03-02
+ */
+public class RedisClusterClientImpl implements EventDao {
+
+ private JedisCluster jedisCluster;
+
+ /**
+ * redis 集群
+ *
+ * @param backendConfig 事件配置
+ */
+ public RedisClusterClientImpl(BackendConfig backendConfig) {
+ Set nodes = new HashSet<>();
+ String[] addressList = backendConfig.getUrl().split(";");
+ for (String address : addressList) {
+ nodes.add(new HostAndPort(
+ Arrays.asList(address.split(":")).get(0),
+ Integer.parseInt(Arrays.asList(address.split(":")).get(1))));
+ }
+ jedisCluster = new JedisCluster(nodes, backendConfig.getUser(), backendConfig.getPassword());
+ }
+
+ @Override
+ public boolean addEvent(Event event) {
+ return false;
+ }
+
+ @Override
+ public boolean addInstanceMeta(InstanceMeta agentInstanceMeta) {
+ return false;
+ }
+
+ @Override
+ public List queryEvent(EventsRequestEntity eventsRequestEntity) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List queryEventPage(String sessionId, int page) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public QueryCacheSizeEntity getQueryCacheSize(EventsRequestEntity eventsRequestEntity) {
+ return new QueryCacheSizeEntity();
+ }
+
+ @Override
+ public void cleanOverDueEventTimerTask() {
+
+ }
+}
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/InstanceMeta.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/InstanceMeta.java
index a21ad06766..84430a41b3 100644
--- a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/InstanceMeta.java
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/InstanceMeta.java
@@ -42,7 +42,7 @@ public class InstanceMeta {
/**
* 应用
*/
- private String application;
+ private String service;
/**
* 节点
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/EventsRequestEntity.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/EventsRequestEntity.java
new file mode 100644
index 0000000000..9e48c58754
--- /dev/null
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/EventsRequestEntity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed 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 com.huaweicloud.sermant.backend.entity.event;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * 事件查询请求实体
+ *
+ * @since 2023-03-02
+ * @author xuezechao
+ */
+@Getter
+@Setter
+public class EventsRequestEntity {
+
+ /**
+ * 应用名
+ */
+ private List service;
+
+ /**
+ * 地址
+ */
+ private List ip;
+
+ /**
+ * 范围
+ */
+ private List scope;
+
+ /**
+ * 类型
+ */
+ private List type;
+
+ /**
+ * 级别
+ */
+ private List level;
+
+ /**
+ * 开始时间
+ */
+ private long startTime;
+
+ /**
+ * 截止时间
+ */
+ private long endTime;
+
+ /**
+ * session id
+ */
+ private String sessionId;
+}
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryCacheSizeEntity.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryCacheSizeEntity.java
new file mode 100644
index 0000000000..78c7d31efb
--- /dev/null
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryCacheSizeEntity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed 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 com.huaweicloud.sermant.backend.entity.event;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 事件查询数量实体
+ *
+ * @author xuezechao
+ * @since 2023-03-02
+ */
+@Getter
+@Setter
+public class QueryCacheSizeEntity {
+
+ int emergencyNum;
+
+ int importantNum;
+
+ int normalNum;
+
+ int total;
+}
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryResultEventInfoEntity.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryResultEventInfoEntity.java
new file mode 100644
index 0000000000..8249c9324b
--- /dev/null
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/event/QueryResultEventInfoEntity.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed 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 com.huaweicloud.sermant.backend.entity.event;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.HashMap;
+
+/**
+ * 查询事件结果实体
+ *
+ * @author xuezechao
+ * @since 2023-03-02
+ */
+@Getter
+@Setter
+public class QueryResultEventInfoEntity {
+
+ /**
+ * 元数据
+ */
+ HashMap meta;
+
+ /**
+ * 事件上报事件
+ */
+ private long time;
+
+ /**
+ * 事件范围
+ */
+ private String scope;
+
+ /**
+ * 事件级别
+ */
+ private String level;
+
+ /**
+ * 事件类型
+ */
+ private String type;
+
+ /**
+ * 事件信息
+ */
+ private Object info;
+}
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/heartbeat/HeartbeatMessage.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/heartbeat/HeartbeatMessage.java
index c2d386bdfe..ddf9e7bc4c 100644
--- a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/heartbeat/HeartbeatMessage.java
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/entity/heartbeat/HeartbeatMessage.java
@@ -31,7 +31,7 @@
* @since 2022-03-19
*/
public class HeartbeatMessage {
- private String appName;
+ private String service;
private String appType;
@@ -70,12 +70,12 @@ public void setIp(List ip) {
this.ip = ip;
}
- public String getAppName() {
- return appName;
+ public String getService() {
+ return service;
}
- public void setAppName(String appName) {
- this.appName = appName;
+ public void setService(String service) {
+ this.service = service;
}
public String getAppType() {
diff --git a/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/util/StringUtils.java b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/util/StringUtils.java
new file mode 100644
index 0000000000..fcbf4e4214
--- /dev/null
+++ b/sermant-backend/src/main/java/com/huaweicloud/sermant/backend/util/StringUtils.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed 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 com.huaweicloud.sermant.backend.util;
+
+/**
+ * 字符串工具类
+ *
+ * @author xuezechao
+ * @since 2023-03-02
+ */
+public class StringUtils {
+
+ /**
+ * 构造函数
+ */
+ private StringUtils() {
+
+ }
+
+ /**
+ * 字符串判空
+ *
+ * @param val 字符串
+ * @return 是否为空
+ */
+ public static boolean isEmpty(String val) {
+ return val == null || "".equals(val.trim());
+ }
+
+ /**
+ * 字符串转int 空返回0
+ *
+ * @param val 字符串
+ * @return 字符串转int值
+ */
+ public static int filterStr(String val) {
+ return val == null ? 0 : Integer.parseInt(val);
+ }
+}
diff --git a/sermant-backend/src/main/resources/META-INF/services/com.huaweicloud.sermant.backend.webhook.WebHookClient b/sermant-backend/src/main/resources/META-INF/services/com.huaweicloud.sermant.backend.webhook.WebHookClient
new file mode 100644
index 0000000000..e25ff4830c
--- /dev/null
+++ b/sermant-backend/src/main/resources/META-INF/services/com.huaweicloud.sermant.backend.webhook.WebHookClient
@@ -0,0 +1,2 @@
+com.huaweicloud.sermant.backend.webhook.feishu.FeiShuHookClient
+com.huaweicloud.sermant.backend.webhook.dingding.DingDingHookClient
\ No newline at end of file
diff --git a/sermant-backend/src/test/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedisTest.java b/sermant-backend/src/test/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedisTest.java
new file mode 100644
index 0000000000..fa15b1a19b
--- /dev/null
+++ b/sermant-backend/src/test/java/com/huaweicloud/sermant/backend/dao/redis/EventDaoForRedisTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023-2023 Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * Licensed 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 com.huaweicloud.sermant.backend.dao.redis;
+
+import com.huaweicloud.sermant.backend.common.conf.CommonConst;
+import com.huaweicloud.sermant.backend.common.conf.BackendConfig;
+import com.huaweicloud.sermant.backend.entity.ClusterEntity;
+import com.huaweicloud.sermant.backend.entity.EnvironmentEntity;
+import com.huaweicloud.sermant.backend.entity.InstanceMeta;
+import com.huaweicloud.sermant.backend.entity.NodeEntity;
+import com.huaweicloud.sermant.backend.entity.event.Event;
+import com.huaweicloud.sermant.backend.entity.event.EventInfo;
+import com.huaweicloud.sermant.backend.entity.event.EventLevel;
+import com.huaweicloud.sermant.backend.entity.event.EventType;
+import com.huaweicloud.sermant.backend.entity.event.EventsRequestEntity;
+import com.huaweicloud.sermant.backend.entity.event.LogInfo;
+
+import com.alibaba.fastjson.JSONObject;
+
+import redis.clients.jedis.Jedis;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.util.Date;
+
+/**
+ * EventDaoForRedis Tester.
+ *
+ * @author xuezechao
+ * @since 2023-03-14
+ */
+@PrepareForTest(EventDaoForRedis.class)
+@RunWith(PowerMockRunner.class)
+public class EventDaoForRedisTest {
+ BackendConfig backendConfig = new BackendConfig();
+ Jedis jedis;
+ Event event = new Event();
+ InstanceMeta instanceMeta = new InstanceMeta();
+ EventDaoForRedis eventDaoForRedis;
+ EventsRequestEntity eventsRequestEntity = new EventsRequestEntity();
+
+ @Before
+ public void before() throws Exception {
+ backendConfig.setUrl("");
+ backendConfig.setPassword("");
+ jedis = Mockito.mock(Jedis.class);
+ PowerMockito.whenNew(Jedis.class).withAnyArguments().thenReturn(jedis);
+ eventDaoForRedis = new EventDaoForRedis(backendConfig);
+ eventsRequestEntity.setStartTime(new Date().getTime());
+
+ event.setTime(new Date().getTime());
+ event.setScope("scope");
+ event.setMetaHash("hashMeat");
+ event.setEventLevel(EventLevel.NORMAL);
+
+ instanceMeta.setInstanceId("instanceId");
+ instanceMeta.setService("application");
+ instanceMeta.setAz("az");
+ NodeEntity nodeEntity = new NodeEntity();
+ nodeEntity.setIp("127.0.0.1");
+ instanceMeta.setNode(nodeEntity);
+ EnvironmentEntity environmentEntity = new EnvironmentEntity();
+ environmentEntity.setEnv("env");
+ instanceMeta.setEnvironment(environmentEntity);
+ ClusterEntity clusterEntity = new ClusterEntity();
+ clusterEntity.setCluster("cluster");
+ instanceMeta.setCluster(clusterEntity);
+
+ eventsRequestEntity.setEndTime(new Date().getTime());
+ }
+
+ @After
+ public void after() {
+ jedis.close();
+ }
+
+ @Test
+ public void testOtherType() {
+ event.setEventType(EventType.OPERATION);
+ EventInfo eventInfo = new EventInfo();
+ eventInfo.setName("name");
+ eventInfo.setDescription("description");
+ event.setEventInfo(eventInfo);
+ setReturn(getEventField(instanceMeta, event));
+
+ eventDaoForRedis.addInstanceMeta(instanceMeta);
+ Assert.assertTrue(eventDaoForRedis.addInstanceMeta(instanceMeta));
+ Assert.assertTrue(eventDaoForRedis.addEvent(event));
+ }
+
+ @Test
+ public void testLogType() {
+ event.setEventType(EventType.LOG);
+ LogInfo logInfo = new LogInfo();
+ logInfo.setLogLevel("logLevel");
+ logInfo.setLogClass("logClass");
+ logInfo.setLogMessage("logMessage");
+ logInfo.setLogThreadId(1);
+ logInfo.setLogMethod("logMethod");
+ logInfo.setThrowable(new Throwable());
+ event.setLogInfo(logInfo);
+ setReturn(getEventField(instanceMeta, event));
+
+ Assert.assertTrue(eventDaoForRedis.addInstanceMeta(instanceMeta));
+ Assert.assertTrue(eventDaoForRedis.addEvent(event));
+ }
+
+ public String getEventField(InstanceMeta instanceMeta, Event event) {
+ return String.join(CommonConst.JOIN_REDIS_KEY,
+ instanceMeta.getInstanceId(),
+ instanceMeta.getService(),
+ instanceMeta.getNode().getIp(),
+ instanceMeta.getCluster().getCluster(),
+ instanceMeta.getEnvironment().getEnv(),
+ instanceMeta.getAz(),
+ event.getMetaHash(),
+ String.valueOf(event.getEventType().getType()),
+ event.getScope(),
+ String.valueOf(event.getTime()));
+ }
+
+ public void setReturn(String eventField) {
+ Mockito.doReturn(1L).when(jedis).hset(CommonConst.REDIS_EVENT_KEY, eventField, JSONObject.toJSONString(event));
+ Mockito.doReturn(1L).when(jedis).zadd(CommonConst.REDIS_EVENT_FIELD_SET_KEY, event.getTime(), eventField);
+ Mockito.doReturn(1L).when(jedis).hset(CommonConst.REDIS_HASH_KEY_OF_INSTANCE_META,
+ instanceMeta.getMetaHash(), JSONObject.toJSONString(instanceMeta));
+ Mockito.doReturn(1L).when(jedis).hdel(CommonConst.REDIS_EVENT_KEY, eventField);
+ Mockito.doReturn(1L).when(jedis).zrem(CommonConst.REDIS_EVENT_FIELD_SET_KEY, eventField);
+ Mockito.doReturn(1L).when(jedis).hdel(CommonConst.REDIS_HASH_KEY_OF_INSTANCE_META, instanceMeta.getMetaHash());
+ Mockito.doReturn(JSONObject.toJSONString(instanceMeta)).when(jedis).hget(CommonConst.REDIS_HASH_KEY_OF_INSTANCE_META, event.getMetaHash());
+ }
+}