Compare commits

..

11 Commits

Author SHA1 Message Date
tomsun28
0df234483d [monitor]feature:param yml support key-value map 2022-04-03 21:29:14 +08:00
tomsun28
0cf66f32ff [manager,collector]feature 修改默认超时时间3000毫秒为6000毫秒 (#55) 2022-04-02 23:02:37 +08:00
tomsun28
fbf7ebd834 feature 检测网站SSL证书是否过期 (#50)
* [collector]feature 检测网站SSL证书是否过期

* [collector]fix cannot find symbol class BASE64Decoder
2022-04-02 21:22:16 +08:00
tomsun28
327f527082 [manager,collector]feature linux监控支持设置超时时间 (#49) 2022-04-02 17:57:25 +08:00
tomsun28
2f52ff5e63 [docs]新增服务器采集节点赞助 2022-04-01 18:00:24 +08:00
tomsun28
abe24914d3 [home]新增oracle监控帮助文档 2022-03-31 17:29:37 +08:00
tomsun28
491ca17106 [docs]补充sureness配置文档 避免误配导致权限异常 2022-03-30 20:44:00 +08:00
老姜bei
feef3e7054 [docs] 使用docker部署TDengine,开放tcp访问端口!16
* 使用docker部署TDengine,开放tcp访问端口
2022-03-30 04:53:32 +00:00
tomsun28
5c7bb4b14e [manager]oracle监控支持tablespace,连接数,qps,tps等指标 2022-03-26 21:29:08 +08:00
tomsun28
bb636c9bae [collector,manager]oracle使用ojdbc8驱动,更新采集指标 2022-03-24 21:54:39 +08:00
xgf
d871572438 [collector,manager]feature 支持oracle数据库监控类型-xgf !15
* [manager]
* [manager]恢复数据库连接
* [manager]恢复数据库连接
* Merge remote-tracking branch 'origin/feature#xgf' into feature#oracle
* [collector,manager]feature 支持oracle数据库监控类型
* Update README.md
* [manager]feature readme.rd 添加默认账号密码 提示
2022-03-23 06:46:15 +00:00
48 changed files with 586 additions and 59 deletions

View File

@@ -76,7 +76,7 @@
##### 安装TDengine ##### 安装TDengine
1. docker安装TDengine 1. docker安装TDengine
`docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp --name tdengine tdengine/tdengine:2.4.0.12` `docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/tcp -p 6030-6049:6030-6049/udp --name tdengine tdengine/tdengine:2.4.0.12`
2. 创建名称为hertzbeat的数据库 2. 创建名称为hertzbeat的数据库
详细步骤参考 [依赖服务TDengine安装初始化](https://hertzbeat.com/docs/start/tdengine-init) 详细步骤参考 [依赖服务TDengine安装初始化](https://hertzbeat.com/docs/start/tdengine-init)
@@ -138,7 +138,8 @@ HertzBeat赫兹跳动为 [Dromara开源社区](https://dromara.org/) 孵化项
##### 赞助 ##### 赞助
感谢[吉实信息(构建全新的微波+光交易网络)](https://www.flarespeed.com)赞助服务器采集节点 感谢[吉实信息(构建全新的微波+光交易网络)](https://www.flarespeed.com) 赞助服务器采集节点
感谢[天上云计算(全新智慧上云)](https://www.tsyvps.com/aff/BZBEGYLX) 赞助服务器采集节点
## 🛡️ License ## 🛡️ License
[`Apache License, Version 2.0`](https://www.apache.org/licenses/LICENSE-2.0.html) [`Apache License, Version 2.0`](https://www.apache.org/licenses/LICENSE-2.0.html)

View File

@@ -109,12 +109,11 @@
<artifactId>mssql-jdbc</artifactId> <artifactId>mssql-jdbc</artifactId>
<version>10.2.0.jre8</version> <version>10.2.0.jre8</version>
</dependency> </dependency>
<!-- oracle --> <!-- oracle -->
<dependency> <dependency>
<groupId>com.oracle</groupId> <groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc6</artifactId> <artifactId>ojdbc8</artifactId>
<version>11.2.0.3</version> <version>21.5.0.0</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@@ -17,7 +17,9 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@@ -75,7 +77,18 @@ public class CommonHttpClient {
@Override @Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { }
@Override @Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
// 判断服务器证书有效期时间
Date now = new Date();
if (x509Certificates != null && x509Certificates.length > 0) {
for (X509Certificate certificate : x509Certificates) {
Date deadline = certificate.getNotAfter();
if (deadline != null && now.after(deadline)) {
throw new CertificateExpiredException();
}
}
}
}
@Override @Override
public X509Certificate[] getAcceptedIssuers() { return null; } public X509Certificate[] getAcceptedIssuers() { return null; }
}; };

View File

@@ -2,6 +2,9 @@ package com.usthe.collector.collect.common.ssh;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.sshd.client.SshClient; import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.keyverifier.AcceptAllServerKeyVerifier;
import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.core.CoreModuleProperties;
/** /**
* ssh公共client * ssh公共client
@@ -16,6 +19,14 @@ public class CommonSshClient {
static { static {
sshClient = SshClient.setUpDefaultClient(); sshClient = SshClient.setUpDefaultClient();
// 接受所有服务端公钥校验会打印warn日志 Server at {} presented unverified {} key: {}
AcceptAllServerKeyVerifier verifier = AcceptAllServerKeyVerifier.INSTANCE;
sshClient.setServerKeyVerifier(verifier);
// 设置链接保活心跳10000毫秒一次, 客户端等待保活心跳超时响应时间3000毫秒
PropertyResolverUtils.updateProperty(
sshClient, CoreModuleProperties.HEARTBEAT_INTERVAL.getName(), 10000);
PropertyResolverUtils.updateProperty(
sshClient, CoreModuleProperties.HEARTBEAT_REPLY_WAIT.getName(), 3000);
sshClient.start(); sshClient.start();
} }

View File

@@ -52,8 +52,8 @@ public class JdbcCommonCollect extends AbstractCollect {
} }
JdbcProtocol jdbcProtocol = metrics.getJdbc(); JdbcProtocol jdbcProtocol = metrics.getJdbc();
String databaseUrl = constructDatabaseUrl(jdbcProtocol); String databaseUrl = constructDatabaseUrl(jdbcProtocol);
// 查询超时时间默认3000毫秒 // 查询超时时间默认6000毫秒
int timeout = 3000; int timeout = 6000;
try { try {
// 获取查询语句超时时间 // 获取查询语句超时时间
if (jdbcProtocol.getTimeout() != null) { if (jdbcProtocol.getTimeout() != null) {

View File

@@ -37,8 +37,8 @@ public class IcmpCollectImpl extends AbstractCollect {
return; return;
} }
IcmpProtocol icmp = metrics.getIcmp(); IcmpProtocol icmp = metrics.getIcmp();
// 超时时间默认300毫秒 // 超时时间默认6000毫秒
int timeout = 300; int timeout = 6000;
try { try {
timeout = Integer.parseInt(icmp.getTimeout()); timeout = Integer.parseInt(icmp.getTimeout());
} catch (Exception e) { } catch (Exception e) {

View File

@@ -5,6 +5,7 @@ import com.usthe.collector.collect.common.cache.CacheIdentifier;
import com.usthe.collector.collect.common.cache.CommonCache; import com.usthe.collector.collect.common.cache.CommonCache;
import com.usthe.collector.collect.common.ssh.CommonSshClient; import com.usthe.collector.collect.common.ssh.CommonSshClient;
import com.usthe.collector.util.CollectorConstants; import com.usthe.collector.util.CollectorConstants;
import com.usthe.collector.util.KeyPairUtil;
import com.usthe.common.entity.job.Metrics; import com.usthe.common.entity.job.Metrics;
import com.usthe.common.entity.job.protocol.SshProtocol; import com.usthe.common.entity.job.protocol.SshProtocol;
import com.usthe.common.entity.message.CollectRep; import com.usthe.common.entity.message.CollectRep;
@@ -19,6 +20,7 @@ import org.springframework.util.StringUtils;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.ConnectException; import java.net.ConnectException;
import java.security.KeyPair;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -56,8 +58,8 @@ public class SshCollectImpl extends AbstractCollect {
return; return;
} }
SshProtocol sshProtocol = metrics.getSsh(); SshProtocol sshProtocol = metrics.getSsh();
// 超时时间默认300毫秒 // 超时时间默认6000毫秒
int timeout = 3000; int timeout = 6000;
try { try {
timeout = Integer.parseInt(sshProtocol.getTimeout()); timeout = Integer.parseInt(sshProtocol.getTimeout());
} catch (Exception e) { } catch (Exception e) {
@@ -181,6 +183,13 @@ public class SshCollectImpl extends AbstractCollect {
.verify(timeout, TimeUnit.MILLISECONDS).getSession(); .verify(timeout, TimeUnit.MILLISECONDS).getSession();
if (StringUtils.hasText(sshProtocol.getPassword())) { if (StringUtils.hasText(sshProtocol.getPassword())) {
clientSession.addPasswordIdentity(sshProtocol.getPassword()); clientSession.addPasswordIdentity(sshProtocol.getPassword());
} else if (StringUtils.hasText(sshProtocol.getPublicKey())) {
KeyPair keyPair = KeyPairUtil.getKeyPairFromPublicKey(sshProtocol.getPublicKey());
if (keyPair != null) {
clientSession.addPublicKeyIdentity(keyPair);
}
} else {
throw new IllegalArgumentException("需填写账户登陆密码或公钥");
} }
// 进行认证 // 进行认证
if (!clientSession.auth().verify(timeout, TimeUnit.MILLISECONDS).isSuccess()) { if (!clientSession.auth().verify(timeout, TimeUnit.MILLISECONDS).isSuccess()) {

View File

@@ -38,8 +38,8 @@ public class TelnetCollectImpl extends AbstractCollect {
} }
TelnetProtocol telnet = metrics.getTelnet(); TelnetProtocol telnet = metrics.getTelnet();
// 超时时间默认300毫秒 // 超时时间默认6000毫秒
int timeout = 300; int timeout = 6000;
try { try {
timeout = Integer.parseInt(telnet.getTimeout()); timeout = Integer.parseInt(telnet.getTimeout());
} catch (Exception e) { } catch (Exception e) {

View File

@@ -12,6 +12,7 @@ import com.usthe.common.entity.job.Job;
import com.usthe.common.entity.job.Metrics; import com.usthe.common.entity.job.Metrics;
import com.usthe.common.util.AesUtil; import com.usthe.common.util.AesUtil;
import com.usthe.common.util.CommonConstants; import com.usthe.common.util.CommonConstants;
import com.usthe.common.util.GsonUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList; import java.util.ArrayList;
@@ -84,6 +85,26 @@ public class WheelTimerTask implements TimerTask {
while (iterator.hasNext()) { while (iterator.hasNext()) {
Map.Entry<String, JsonElement> entry = iterator.next(); Map.Entry<String, JsonElement> entry = iterator.next();
JsonElement element = entry.getValue(); JsonElement element = entry.getValue();
String key = entry.getKey();
// 替换KEY-VALUE情况的属性 比如http headers params
if (key != null && key.startsWith("^_^") && key.endsWith("^_^")) {
key = key.replaceAll("\\^_\\^", "");
Configmap param = configmap.get(key);
if (param.getType() == (byte) 3) {
String jsonValue = (String) param.getValue();
Map<String, String> map = GsonUtil.fromJson(jsonValue, Map.class);
if (map != null) {
map.forEach((name, value) -> {
if (name != null && !"".equals(name.trim())) {
jsonObject.addProperty(name, value);
}
});
}
}
iterator.remove();
continue;
}
// 替换正常的VALUE值
if (element.isJsonPrimitive()) { if (element.isJsonPrimitive()) {
// 判断是否含有特殊字符 替换 // 判断是否含有特殊字符 替换
String value = element.getAsString(); String value = element.getAsString();

View File

@@ -0,0 +1,48 @@
package com.usthe.collector.util;
import lombok.extern.slf4j.Slf4j;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* 密钥工具类
* @author tom
* @date 2022/4/2 17:04
*/
@Slf4j
public class KeyPairUtil {
private static KeyFactory keyFactory;
static {
try {
keyFactory = KeyFactory.getInstance("RSA");
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
/**
* 获取密钥对
*/
public static KeyPair getKeyPairFromPublicKey(String publicKeyStr) {
try {
if (publicKeyStr == null || "".equals(publicKeyStr)) {
return null;
}
// todo fix 公钥解析
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return new KeyPair(publicKey, null);
} catch (Exception e) {
log.info("[keyPair] parse failed, {}." + e.getMessage());
return null;
}
}
}

View File

@@ -28,7 +28,7 @@ public class Configmap {
private Object value; private Object value;
/** /**
* 参数类型 0:数字 1:字符串 2:加密串 * 参数类型 0:数字 1:字符串 2:加密串 3:map映射的json串
* number,string,secret * number,string,secret
* 数字,非加密字符串,加密字符串 * 数字,非加密字符串,加密字符串
*/ */

View File

@@ -29,7 +29,7 @@ public class SshProtocol {
/** /**
* 超时时间 * 超时时间
*/ */
private String timeout = "3000"; private String timeout;
/** /**
* 用户名 * 用户名

View File

@@ -59,15 +59,15 @@ public class Param {
* 参数值 * 参数值
*/ */
@ApiModelProperty(value = "参数值", example = "8080", accessMode = READ_WRITE, position = 3) @ApiModelProperty(value = "参数值", example = "8080", accessMode = READ_WRITE, position = 3)
@Length(max = 255) @Length(max = 8126)
private String value; private String value;
/** /**
* 参数类型 0:数字 1:字符串 2:加密串 * 参数类型 0:数字 1:字符串 2:加密串 3:map映射的json串
*/ */
@ApiModelProperty(value = "参数类型 0:数字 1:字符串 2:加密串", accessMode = READ_WRITE, position = 4) @ApiModelProperty(value = "参数类型 0:数字 1:字符串 2:加密串 3:map映射的json串", accessMode = READ_WRITE, position = 4)
@Min(0) @Min(0)
@Max(2) @Max(3)
private byte type; private byte type;
/** /**

View File

@@ -110,29 +110,41 @@ public class ParamDefine {
@Convert(converter = JsonOptionListAttributeConverter.class) @Convert(converter = JsonOptionListAttributeConverter.class)
private List<Option> options; private List<Option> options;
/**
* 当type为key-value时有效,表示key的别名描述
*/
@ApiModelProperty(value = "当type为key-value时有效,表示key的别名描述", example = "Name", accessMode = READ_WRITE, position = 9)
private String keyAlias;
/**
* 当type为key-value时有效,表示value的别名描述
*/
@ApiModelProperty(value = "当type为key-value时有效,表示value的别名描述", example = "Value", accessMode = READ_WRITE, position = 10)
private String valueAlias;
/** /**
* 此条记录创建者 * 此条记录创建者
*/ */
@ApiModelProperty(value = "此条记录创建者", example = "tom", accessMode = READ_ONLY, position = 9) @ApiModelProperty(value = "此条记录创建者", example = "tom", accessMode = READ_ONLY, position = 11)
private String creator; private String creator;
/** /**
* 此条记录最新修改者 * 此条记录最新修改者
*/ */
@ApiModelProperty(value = "此条记录最新修改者", example = "tom", accessMode = READ_ONLY, position = 10) @ApiModelProperty(value = "此条记录最新修改者", example = "tom", accessMode = READ_ONLY, position = 12)
private String modifier; private String modifier;
/** /**
* 记录创建时间 * 记录创建时间
*/ */
@ApiModelProperty(value = "记录创建时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY, position = 11) @ApiModelProperty(value = "记录创建时间(毫秒时间戳)", example = "1612198922000", accessMode = READ_ONLY, position = 13)
@Column(insertable = false, updatable = false) @Column(insertable = false, updatable = false)
private LocalDateTime gmtCreate; private LocalDateTime gmtCreate;
/** /**
* 记录最新修改时间 * 记录最新修改时间
*/ */
@ApiModelProperty(value = "记录最新修改时间(毫秒时间戳)", example = "1612198444000", accessMode = READ_ONLY, position = 12) @ApiModelProperty(value = "记录最新修改时间(毫秒时间戳)", example = "1612198444000", accessMode = READ_ONLY, position = 14)
@Column(insertable = false, updatable = false) @Column(insertable = false, updatable = false)
private LocalDateTime gmtUpdate; private LocalDateTime gmtUpdate;

View File

@@ -18,7 +18,7 @@ sidebar_label: 帮助入门
### 数据库监控 ### 数据库监控
[MYSQL数据库监控](mysql) &emsp;&emsp;&emsp;&emsp; [MariaDB数据库监控](mariadb) &emsp;&emsp;&emsp;&emsp; [PostgreSQL数据库监控](postgresql) &emsp;&emsp;&emsp;&emsp; [SqlServer数据库监控](sqlserver) [MYSQL数据库监控](mysql) &emsp;&emsp;&emsp;&emsp; [MariaDB数据库监控](mariadb) &emsp;&emsp;&emsp;&emsp; [PostgreSQL数据库监控](postgresql) &emsp;&emsp;&emsp;&emsp; [SqlServer数据库监控](sqlserver) &emsp;&emsp;&emsp;&emsp; [Oracle数据库监控](oracle)
### 操作系统监控 ### 操作系统监控

62
home/docs/help/oracle.md Normal file
View File

@@ -0,0 +1,62 @@
---
id: oracle
title: 监控ORACLE数据库监控
sidebar_label: ORACLE数据库
---
> 对ORACLE数据库的通用性能指标进行采集监控。
### 配置参数
| 参数名称 | 参数帮助描述 |
| ----------- | ----------- |
| 监控Host | 被监控的对端IPV4IPV6或域名。注意⚠不带协议头(eg: https://, http://)。 |
| 监控名称 | 标识此监控的名称,名称需要保证唯一性。 |
| 端口 | 数据库对外提供的端口默认为1521。 |
| 查询超时时间 | 设置SQL查询未响应数据时的超时时间单位ms毫秒默认3000毫秒。 |
| 数据库名称 | 数据库实例名称,可选。 |
| 用户名 | 数据库连接用户名,可选 |
| 密码 | 数据库连接密码,可选 |
| URL | 数据库连接URL可选若配置则URL里面的数据库名称用户名密码等参数会覆盖上面配置的参数 |
| 采集间隔 | 监控周期性采集数据间隔时间单位秒可设置的最小间隔为10秒 |
| 是否探测 | 新增监控前是否先探测检查监控可用性,探测成功才会继续新增修改操作 |
| 描述备注 | 更多标识和描述此监控的备注信息,用户可以在这里备注信息 |
### 采集指标
#### 指标集合basic
| 指标名称 | 指标单位 | 指标帮助描述 |
| ----------- | ----------- | ----------- |
| database_version | 无 | 数据库版本 |
| database_type | 无 | 数据库类型 |
| hostname | 无 | 主机名称 |
| instance_name | 无 | 数据库实例名称 |
| startup_time | 无 | 数据库启动时间 |
| status | 无 | 数据库状态 |
#### 指标集合tablespace
| 指标名称 | 指标单位 | 指标帮助描述 |
| ----------- | ----------- | ----------- |
| file_id | 无 | 文件ID |
| file_name | 无 | 文件名称 |
| tablespace_name | 无 | 所属表空间名称 |
| status | 无 | 状态 |
| bytes | MB | 大小 |
| blocks | 无 | 区块数量 |
#### 指标集合user_connect
| 指标名称 | 指标单位 | 指标帮助描述 |
| ----------- | ----------- | ----------- |
| username | 无 | 用户名 |
| counts | 个数 | 当前连接数量 |
#### 指标集合performance
| 指标名称 | 指标单位 | 指标帮助描述 |
| ----------- | ----------- | ----------- |
| qps | QPS | I/O Requests per Second 每秒IO请求数量 |
| tps | TPS | User Transaction Per Sec 每秒用户事物处理数量 |
| mbps | MBPS | I/O Megabytes per Second 每秒 I/O 兆字节数量 |

View File

@@ -44,9 +44,55 @@ sidebar_label: Docker方式部署
若需要新增删除修改账户或密码,可以通过配置 `sureness.yml` 实现,若无此需求可忽略此步骤 若需要新增删除修改账户或密码,可以通过配置 `sureness.yml` 实现,若无此需求可忽略此步骤
在主机目录下创建sureness.ymleg:/opt/sureness.yml 在主机目录下创建sureness.ymleg:/opt/sureness.yml
配置文件内容参考 项目仓库[/script/sureness.yml](https://gitee.com/dromara/hertzbeat/blob/master/script/sureness.yml) 配置文件内容参考 项目仓库[/script/sureness.yml](https://gitee.com/dromara/hertzbeat/blob/master/script/sureness.yml)
修改sureness.yml的如下部分参数[注意⚠sureness配置的其它默认参数需保留]
```yaml ```yaml
resourceRole:
- /account/auth/refresh===post===[role1,role2,role3,role4]
excludedResource:
- /account/auth/**===*
- /===get
- /i18n/**===get
- /apps/hierarchy===get
- /console/**===get
- /**/*.html===get
- /**/*.js===get
- /**/*.css===get
- /**/*.ico===get
- /**/*.ttf===get
- /**/*.png===get
- /**/*.gif===get
- /**/*.png===*
- /swagger-resources/**===get
- /v2/api-docs===get
- /v3/api-docs===get
# 用户账户信息
# 下面有 admin tom lili 三个账户
# eg: admin 拥有[role1,role2]角色,密码为admin
# eg: tom 拥有[role1,role2,role3],密码为tom@123
# eg: lili 拥有[role1,role2],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289
account:
- appId: admin
credential: admin
role: [role1,role2]
- appId: tom
credential: tom@123
role: [role1,role2,role3]
- appId: lili
# 注意 Digest认证不支持加盐加密的密码账户
# 加盐加密的密码,通过 MD5(password+salt)计算
# 此账户的原始密码为 lili
credential: 1A676730B0C7F54654B0E09184448289
salt: 123
role: [role1,role2]
```
修改sureness.yml的如下**部分参数****[注意⚠sureness配置的其它默认参数需保留]**
```yaml
# 用户账户信息 # 用户账户信息
# 下面有 admin tom lili 三个账户 # 下面有 admin tom lili 三个账户
# eg: admin 拥有[role1,role2]角色,密码为admin # eg: admin 拥有[role1,role2]角色,密码为admin

View File

@@ -46,7 +46,7 @@ sidebar_label: 安装包方式部署
4. 配置用户配置文件(非必须,配置账户需要) 4. 配置用户配置文件(非必须,配置账户需要)
HertzBeat默认内置三个用户账户,分别为 admin/admin tom/tom@123 lili/lili HertzBeat默认内置三个用户账户,分别为 admin/admin tom/tom@123 lili/lili
若需要新增删除修改账户或密码,可以通过修改位于 `hertzbeat/config/sureness.yml` 的配置文件实现,若无此需求可忽略此步骤 若需要新增删除修改账户或密码,可以通过修改位于 `hertzbeat/config/sureness.yml` 的配置文件实现,若无此需求可忽略此步骤
修改sureness.yml的如下部分参数[注意⚠sureness配置的其它默认参数需保留] 修改sureness.yml的如下**部分参数****[注意⚠sureness配置的其它默认参数需保留]**
```yaml ```yaml
# 用户账户信息 # 用户账户信息

View File

@@ -65,7 +65,8 @@
"help/mysql", "help/mysql",
"help/mariadb", "help/mariadb",
"help/postgresql", "help/postgresql",
"help/sqlserver" "help/sqlserver",
"help/oracle"
] ]
}, },
{ {

View File

@@ -236,6 +236,9 @@ public class MonitorServiceImpl implements MonitorService {
case "checkbox": case "checkbox":
// todo checkbox校验 // todo checkbox校验
break; break;
case "key-value":
// todo key-value校验
break;
// todo 更多参数定义与实际值格式校验 // todo 更多参数定义与实际值格式校验
default: default:
throw new IllegalArgumentException("ParamDefine type " + paramDefine.getType() + " is invalid."); throw new IllegalArgumentException("ParamDefine type " + paramDefine.getType() + " is invalid.");

View File

@@ -5,7 +5,7 @@ app: example
name: name:
zh-CN: 模拟应用类型 zh-CN: 模拟应用类型
en-US: EXAMPLE APP en-US: EXAMPLE APP
# 参数映射map. type是参数类型: 0-number数字, 1-string明文字符串, 2-secret加密字符串 # 参数映射map. type是参数类型: 0-number数字, 1-string明文字符串, 2-secret加密字符串, 3-map映射的json串
# 强制固定必须参数 - host # 强制固定必须参数 - host
configmap: configmap:
- key: host - key: host
@@ -16,6 +16,8 @@ configmap:
type: 1 type: 1
- key: password - key: password
type: 2 type: 2
- key: headers
type: 3
# 指标组列表 # 指标组列表
metrics: metrics:
# 第一个监控指标组 cpu # 第一个监控指标组 cpu
@@ -69,7 +71,7 @@ metrics:
ssl: false ssl: false
# 请求头内容 # 请求头内容
headers: headers:
apiVersion: v1 ^_^headers^_^: ^_^headers^_^
# 请求参数内容 # 请求参数内容
params: params:
param1: param1 param1: param1

View File

@@ -16,6 +16,8 @@ configmap:
type: 1 type: 1
- key: password - key: password
type: 2 type: 2
- key: timeout
type: 0
# 指标组列表 # 指标组列表
metrics: metrics:
# 第一个监控指标组 basic # 第一个监控指标组 basic
@@ -44,6 +46,7 @@ metrics:
port: ^_^port^_^ port: ^_^port^_^
username: ^_^username^_^ username: ^_^username^_^
password: ^_^password^_^ password: ^_^password^_^
timeout: ^_^timeout^_^
script: (uname -r ; hostname ; uptime | awk -F "," '{print $1}' | sed "s/ //g") | sed ":a;N;s/\n/^/g;ta" | awk -F '^' 'BEGIN{print "version hostname uptime"} {print $1, $2, $3}' script: (uname -r ; hostname ; uptime | awk -F "," '{print $1}' | sed "s/ //g") | sed ":a;N;s/\n/^/g;ta" | awk -F '^' 'BEGIN{print "version hostname uptime"} {print $1, $2, $3}'
# 响应数据解析方式oneRow, multiRow # 响应数据解析方式oneRow, multiRow
parseType: multiRow parseType: multiRow
@@ -75,6 +78,7 @@ metrics:
port: ^_^port^_^ port: ^_^port^_^
username: ^_^username^_^ username: ^_^username^_^
password: ^_^password^_^ password: ^_^password^_^
timeout: ^_^timeout^_^
script: "LANG=C lscpu | awk -F: '/Model name/ {print $2}';awk '/processor/{core++} END{print core}' /proc/cpuinfo;uptime | sed 's/,/ /g' | awk '{for(i=NF-2;i<=NF;i++)print $i }' | xargs;vmstat 1 1 | awk 'NR==3{print $11}';vmstat 1 1 | awk 'NR==3{print $12}'" script: "LANG=C lscpu | awk -F: '/Model name/ {print $2}';awk '/processor/{core++} END{print core}' /proc/cpuinfo;uptime | sed 's/,/ /g' | awk '{for(i=NF-2;i<=NF;i++)print $i }' | xargs;vmstat 1 1 | awk 'NR==3{print $11}';vmstat 1 1 | awk 'NR==3{print $12}'"
parseType: oneRow parseType: oneRow
@@ -107,6 +111,7 @@ metrics:
port: ^_^port^_^ port: ^_^port^_^
username: ^_^username^_^ username: ^_^username^_^
password: ^_^password^_^ password: ^_^password^_^
timeout: ^_^timeout^_^
script: free -m | grep Mem | awk 'BEGIN{print "total used free buff_cache available"} {print $2,$3,$4,$6,$7}' script: free -m | grep Mem | awk 'BEGIN{print "total used free buff_cache available"} {print $2,$3,$4,$6,$7}'
parseType: multiRow parseType: multiRow
@@ -139,6 +144,7 @@ metrics:
port: ^_^port^_^ port: ^_^port^_^
username: ^_^username^_^ username: ^_^username^_^
password: ^_^password^_^ password: ^_^password^_^
timeout: ^_^timeout^_^
script: vmstat -D | awk 'NR==1{print $1}';vmstat -D | awk 'NR==2{print $1}';vmstat 1 1 | awk 'NR==3{print $10}';vmstat 1 1 | awk 'NR==3{print $9}';vmstat 1 1 | awk 'NR==3{print $16}' script: vmstat -D | awk 'NR==1{print $1}';vmstat -D | awk 'NR==2{print $1}';vmstat 1 1 | awk 'NR==3{print $10}';vmstat 1 1 | awk 'NR==3{print $9}';vmstat 1 1 | awk 'NR==3{print $16}'
parseType: oneRow parseType: oneRow
@@ -164,5 +170,6 @@ metrics:
port: ^_^port^_^ port: ^_^port^_^
username: ^_^username^_^ username: ^_^username^_^
password: ^_^password^_^ password: ^_^password^_^
timeout: ^_^timeout^_^
script: cat /proc/net/dev | tail -n +3 | awk 'BEGIN{ print "interface_name receive_bytes transmit_bytes"} {print $1,$2,$10}' script: cat /proc/net/dev | tail -n +3 | awk 'BEGIN{ print "interface_name receive_bytes transmit_bytes"} {print $1,$2,$10}'
parseType: multiRow parseType: multiRow

View File

@@ -29,11 +29,141 @@ metrics:
# 指标组中的具体监控指标 # 指标组中的具体监控指标
fields: fields:
# 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位 # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位
- field: DST_PRIMARY_TT_VERSION - field: database_version
type: 1 type: 1
instance: true instance: true
- field: NLS_RDBMS_VERSION - field: database_type
type: 1 type: 1
- field: hostname
type: 1
- field: instance_name
type: 1
- field: startup_time
type: 1
- field: status
type: 1
# (非必须)监控指标别名,与上面的指标名映射。用于采集接口数据字段不直接是最终指标名称,需要此别名做映射转换
aliasFields:
- VERSION
- DATABASE_TYPE
- HOST_NAME
- INSTANCE_NAME
- STARTUP_TIME
- STATUS
# (非必须)指标计算表达式,与上面的别名一起作用,计算出最终需要的指标值
# eg: cores=core1+core2, usage=usage, waitTime=allTime-runningTime
calculates:
- database_version=VERSION
- database_type=DATABASE_TYPE
- hostname=HOST_NAME
- instance_name=INSTANCE_NAME
- startup_time=STARTUP_TIME
- status=STATUS
protocol: jdbc
jdbc:
# 主机host: ipv4 ipv6 域名
host: ^_^host^_^
# 端口
port: ^_^port^_^
platform: oracle
username: ^_^username^_^
password: ^_^password^_^
database: ^_^database^_^
timeout: ^_^timeout^_^
# SQL查询方式 oneRow, multiRow, columns
queryType: oneRow
# sql
sql: select * from sys.v_$instance
url: ^_^url^_^
- name: tablespace
priority: 1
# 指标组中的具体监控指标
fields:
# 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位
- field: file_id
type: 1
instance: true
- field: file_name
type: 1
- field: tablespace_name
type: 1
- field: status
type: 1
- field: bytes
type: 0
unit: MB
- field: blocks
type: 0
unit: 块数
protocol: jdbc
jdbc:
# 主机host: ipv4 ipv6 域名
host: ^_^host^_^
# 端口
port: ^_^port^_^
platform: oracle
username: ^_^username^_^
password: ^_^password^_^
database: ^_^database^_^
timeout: ^_^timeout^_^
# SQL查询方式 oneRow, multiRow, columns
queryType: oneRow
# sql
sql: select file_id, file_name, tablespace_name, status, bytes / 1024 / 1024 as bytes, blocks from dba_data_files
url: ^_^url^_^
- name: user_connect
priority: 1
fields:
# 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位
- field: username
type: 1
instance: true
- field: counts
type: 0
unit: 连接数
protocol: jdbc
jdbc:
# 主机host: ipv4 ipv6 域名
host: ^_^host^_^
# 端口
port: ^_^port^_^
platform: oracle
username: ^_^username^_^
password: ^_^password^_^
database: ^_^database^_^
timeout: ^_^timeout^_^
# SQL查询方式 oneRow, multiRow, columns
queryType: oneRow
# sql
sql: SELECT username, count( username ) as counts FROM v$session WHERE username IS NOT NULL GROUP BY username
url: ^_^url^_^
- name: performance
priority: 1
fields:
# 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 instance是否为实例主键 unit:指标单位
- field: qps
type: 0
unit: qps
- field: tps
type: 0
unit: tps
- field: mbps
type: 0
unit: mbps
# (非必须)监控指标别名,与上面的指标名映射。用于采集接口数据字段不直接是最终指标名称,需要此别名做映射转换
aliasFields:
- I/O Requests per Second
- User Transaction Per Sec
- I/O Megabytes per Second
# (非必须)指标计算表达式,与上面的别名一起作用,计算出最终需要的指标值
# eg: cores=core1+core2, usage=usage, waitTime=allTime-runningTime
calculates:
- qps=I/O Requests per Second
- tps=User Transaction Per Sec
- mbps=I/O Megabytes per Second
protocol: jdbc protocol: jdbc
jdbc: jdbc:
# 主机host: ipv4 ipv6 域名 # 主机host: ipv4 ipv6 域名
@@ -48,5 +178,5 @@ metrics:
# SQL查询方式 oneRow, multiRow, columns # SQL查询方式 oneRow, multiRow, columns
queryType: columns queryType: columns
# sql # sql
sql: select * from sys.props$ sql: select metric_name, value from gv$sysmetric where metric_name = 'I/O Megabytes per Second' or metric_name = 'User Transaction Per Sec' or metric_name = 'I/O Requests per Second'
url: ^_^url^_^ url: ^_^url^_^

View File

@@ -49,3 +49,9 @@ param:
value: PUT value: PUT
- label: DELETE请求 - label: DELETE请求
value: DELETE value: DELETE
- field: headers
name: 请求头
type: key-value
required: false
keyAlias: Header Name
valueAlias: Header Value

View File

@@ -11,6 +11,12 @@ param:
required: true required: true
defaultValue: 22 defaultValue: 22
placeholder: '请输入端口' placeholder: '请输入端口'
- field: timeout
name: 超时时间
type: number
required: false
defaultValue: 6000
placeholder: '超时时间'
- field: username - field: username
name: 用户名 name: 用户名
type: text type: text
@@ -19,4 +25,4 @@ param:
- field: password - field: password
name: 密码 name: 密码
type: password type: password
required: true required: false

View File

@@ -15,7 +15,7 @@ param:
name: 查询超时时间 name: 查询超时时间
type: number type: number
required: false required: false
defaultValue: 3000 defaultValue: 6000
placeholder: '查询超时时间' placeholder: '查询超时时间'
- field: database - field: database
name: 数据库名称 name: 数据库名称

View File

@@ -15,7 +15,7 @@ param:
name: 查询超时时间 name: 查询超时时间
type: number type: number
required: false required: false
defaultValue: 3000 defaultValue: 6000
placeholder: '查询超时时间' placeholder: '查询超时时间'
- field: database - field: database
name: 数据库名称 name: 数据库名称

View File

@@ -9,13 +9,13 @@ param:
type: number type: number
range: '[0,65535]' range: '[0,65535]'
required: true required: true
defaultValue: 3306 defaultValue: 1521
placeholder: '请输入端口' placeholder: '请输入端口'
- field: timeout - field: timeout
name: 查询超时时间 name: 查询超时时间
type: number type: number
required: false required: false
defaultValue: 3000 defaultValue: 6000
placeholder: '查询超时时间' placeholder: '查询超时时间'
- field: database - field: database
name: 数据库名称 name: 数据库名称

View File

@@ -17,4 +17,4 @@ param:
range: '[0,100000]' range: '[0,100000]'
required: true required: true
placeholder: '请输入超时时间,单位毫秒' placeholder: '请输入超时时间,单位毫秒'
defaultValue: 3000 defaultValue: 6000

View File

@@ -24,4 +24,4 @@ param:
range: '[0,100000]' range: '[0,100000]'
required: true required: true
placeholder: '请输入超时时间,单位毫秒' placeholder: '请输入超时时间,单位毫秒'
defaultValue: 3000 defaultValue: 6000

View File

@@ -15,7 +15,7 @@ param:
name: 查询超时时间 name: 查询超时时间
type: number type: number
required: false required: false
defaultValue: 3000 defaultValue: 6000
placeholder: '查询超时时间' placeholder: '查询超时时间'
- field: database - field: database
name: 数据库名称 name: 数据库名称

View File

@@ -15,7 +15,7 @@ param:
name: 查询超时时间 name: 查询超时时间
type: number type: number
required: false required: false
defaultValue: 3000 defaultValue: 6000
placeholder: '查询超时时间' placeholder: '查询超时时间'
- field: database - field: database
name: 数据库名称 name: 数据库名称

View File

@@ -24,4 +24,4 @@ param:
range: '[0,100000]' range: '[0,100000]'
required: true required: true
placeholder: '请输入超时时间,单位毫秒' placeholder: '请输入超时时间,单位毫秒'
defaultValue: 3000 defaultValue: 6000

View File

@@ -34,7 +34,7 @@ CREATE TABLE param
id bigint not null auto_increment comment '参数ID', id bigint not null auto_increment comment '参数ID',
monitor_id bigint not null comment '监控ID', monitor_id bigint not null comment '监控ID',
field varchar(100) not null comment '参数标识符', field varchar(100) not null comment '参数标识符',
value varchar(255) comment '参数值,最大字符长度255', value varchar(8126) comment '参数值,最大字符长度8126',
type tinyint not null default 0 comment '参数类型 0:数字 1:字符串 2:加密串', type tinyint not null default 0 comment '参数类型 0:数字 1:字符串 2:加密串',
gmt_create timestamp default current_timestamp comment 'create time', gmt_create timestamp default current_timestamp comment 'create time',
gmt_update datetime default current_timestamp on update current_timestamp comment 'update time', gmt_update datetime default current_timestamp on update current_timestamp comment 'update time',

View File

@@ -36,10 +36,10 @@ excludedResource:
# eg: lili 拥有[role1,role2],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289 # eg: lili 拥有[role1,role2],明文密码为lili, 加盐密码为1A676730B0C7F54654B0E09184448289
account: account:
- appId: admin - appId: admin
credential: admin@123. credential: admin
role: [role1,role2] role: [role1,role2]
- appId: tom - appId: tom
credential: tom@123. credential: tom
role: [role1,role2,role3] role: [role1,role2,role3]
- appId: lili - appId: lili
# 注意 Digest认证不支持加盐加密的密码账户 # 注意 Digest认证不支持加盐加密的密码账户

View File

@@ -34,7 +34,7 @@ CREATE TABLE param
id bigint not null auto_increment comment '参数ID', id bigint not null auto_increment comment '参数ID',
monitor_id bigint not null comment '监控ID', monitor_id bigint not null comment '监控ID',
field varchar(100) not null comment '参数标识符', field varchar(100) not null comment '参数标识符',
value varchar(255) comment '参数值,最大字符长度255', value varchar(8126) comment '参数值,最大字符长度8126',
type tinyint not null default 0 comment '参数类型 0:数字 1:字符串 2:加密串', type tinyint not null default 0 comment '参数类型 0:数字 1:字符串 2:加密串',
gmt_create timestamp default current_timestamp comment 'create time', gmt_create timestamp default current_timestamp comment 'create time',
gmt_update datetime default current_timestamp on update current_timestamp comment 'update time', gmt_update datetime default current_timestamp on update current_timestamp comment 'update time',

View File

@@ -23,7 +23,7 @@ excludedResource:
- /**/*.ttf===get - /**/*.ttf===get
- /**/*.png===get - /**/*.png===get
- /**/*.gif===get - /**/*.gif===get
- /**/*.png===* - /**/*.png===*
# swagger ui 资源 # swagger ui 资源
- /swagger-resources/**===get - /swagger-resources/**===get
- /v2/api-docs===get - /v2/api-docs===get

View File

@@ -9,4 +9,7 @@ export class ParamDefine {
limit: number | undefined; limit: number | undefined;
//'[{"label":"GET请求","value":"GET"},{"label":"PUT请求","value":"PUT"}]' //'[{"label":"GET请求","value":"GET"},{"label":"PUT请求","value":"PUT"}]'
options!: any[]; options!: any[];
// 当type为key-value时有效,表示别名描述
keyAlias!: string;
valueAlias!: string;
} }

View File

@@ -86,7 +86,7 @@
>{{ paramDefine.name }} >{{ paramDefine.name }}
</nz-form-label> </nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'password'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n"> <nz-form-control *ngIf="paramDefine.type === 'password'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<nz-input-group [nzSuffix]="suffixTemplate"> <nz-input-group [nzSuffix]="suffixTemplate" style="width: 100%">
<input <input
[type]="passwordVisible ? 'text' : 'password'" [type]="passwordVisible ? 'text' : 'password'"
nz-input nz-input
@@ -147,6 +147,18 @@
</label> </label>
</nz-radio-group> </nz-radio-group>
</nz-form-control> </nz-form-control>
<nz-form-label *ngIf="paramDefine.type === 'key-value'" nzSpan="7" [nzRequired]="paramDefine.required" [nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'key-value'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<app-key-value-input
[(value)]="params[i].value"
[id]="paramDefine.field"
keyAlias="Header Name"
valueAlias="Header Value"
></app-key-value-input>
</nz-form-control>
</nz-form-item> </nz-form-item>
<nz-divider></nz-divider> <nz-divider></nz-divider>

View File

@@ -78,7 +78,13 @@ export class MonitorEditComponent implements OnInit {
if (param === undefined) { if (param === undefined) {
param = new Param(); param = new Param();
param.field = define.field; param.field = define.field;
param.type = define.type === 'number' ? 0 : 1; if (define.type === 'number') {
param.type = 0;
} else if (define.type === 'key-value') {
param.type = 3;
} else {
param.type = 1;
}
if (define.type === 'boolean') { if (define.type === 'boolean') {
param.value = false; param.value = false;
} }

View File

@@ -95,7 +95,7 @@
>{{ paramDefine.name }} >{{ paramDefine.name }}
</nz-form-label> </nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'password'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n"> <nz-form-control *ngIf="paramDefine.type === 'password'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<nz-input-group [nzSuffix]="suffixTemplate"> <nz-input-group [nzSuffix]="suffixTemplate" style="width: 100%">
<input <input
[type]="passwordVisible ? 'text' : 'password'" [type]="passwordVisible ? 'text' : 'password'"
nz-input nz-input
@@ -156,6 +156,18 @@
</label> </label>
</nz-radio-group> </nz-radio-group>
</nz-form-control> </nz-form-control>
<nz-form-label *ngIf="paramDefine.type === 'key-value'" nzSpan="7" [nzRequired]="paramDefine.required" [nzFor]="paramDefine.field"
>{{ paramDefine.name }}
</nz-form-label>
<nz-form-control *ngIf="paramDefine.type === 'key-value'" nzSpan="8" [nzErrorTip]="'validation.required' | i18n">
<app-key-value-input
[(value)]="params[i].value"
[id]="paramDefine.field"
[keyAlias]="paramDefine.keyAlias ? paramDefine.keyAlias : 'Name'"
[valueAlias]="paramDefine.valueAlias ? paramDefine.valueAlias : 'Value'"
></app-key-value-input>
</nz-form-control>
</nz-form-item> </nz-form-item>
<nz-divider></nz-divider> <nz-divider></nz-divider>

View File

@@ -58,7 +58,13 @@ export class MonitorNewComponent implements OnInit {
this.paramDefines.forEach(define => { this.paramDefines.forEach(define => {
let param = new Param(); let param = new Param();
param.field = define.field; param.field = define.field;
param.type = define.type === 'number' ? 0 : 1; if (define.type === 'number') {
param.type = 0;
} else if (define.type === 'key-value') {
param.type = 3;
} else {
param.type = 1;
}
if (define.type === 'boolean') { if (define.type === 'boolean') {
param.value = false; param.value = false;
} }

View File

@@ -1,2 +0,0 @@
### 公共通用小组件

View File

@@ -0,0 +1,12 @@
<div *ngFor="let item of keyValues; let i = index" nz-row>
<div nz-col nzSpan="10">
<input nz-input [placeholder]="keyAlias" [(ngModel)]="item.key" (ngModelChange)="onChange()" />
</div>
<div nz-col nzSpan="11">
<input nz-input [placeholder]="valueAlias" [(ngModel)]="item.value" (ngModelChange)="onChange()" />
</div>
<div nz-col nzSpan="3">
<i nz-icon nzType="plus-circle" class="dynamic-button" *ngIf="i === 0" (click)="addNew($event)"></i>
<i nz-icon nzType="minus-circle" class="dynamic-button" (click)="removeCurrent(i, $event)"></i>
</div>
</div>

View File

@@ -0,0 +1,12 @@
.dynamic-button {
cursor: pointer;
position: relative;
top: 4px;
font-size: 15px;
transition: all 0.3s;
margin-left: 6%;
}
.dynamic-button:hover {
font-size: 26px;
}

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { KeyValueInputComponent } from './key-value-input.component';
describe('KeyValueInputComponent', () => {
let component: KeyValueInputComponent;
let fixture: ComponentFixture<KeyValueInputComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ KeyValueInputComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(KeyValueInputComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,63 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
@Component({
selector: 'app-key-value-input',
templateUrl: './key-value-input.component.html',
styleUrls: ['./key-value-input.component.less']
})
export class KeyValueInputComponent implements OnInit {
constructor() {}
@Input() value!: any;
@Output() readonly valueChange = new EventEmitter<string>();
@Input()
keyAlias: string = 'Key';
@Input()
valueAlias: string = 'Value';
keyValues: any[] = [];
ngOnInit(): void {
if (this.value == undefined) {
this.value = {
'': ''
};
} else {
this.value = JSON.parse(this.value);
}
Object.keys(this.value).map(item => {
this.keyValues.push({
key: item,
value: this.value[item]
});
});
}
addNew(e?: MouseEvent) {
if (e) {
e.preventDefault();
}
this.keyValues.push({
key: '',
value: ''
});
}
removeCurrent(index: number, e?: MouseEvent) {
if (e) {
e.preventDefault();
}
if (this.keyValues.length > 1) {
this.keyValues.splice(index, 1);
}
}
onChange() {
this.value = {};
this.keyValues.forEach(item => {
if (item != null && item.key != null) {
this.value[item.key] = item.value;
}
});
this.valueChange.emit(JSON.stringify(this.value));
}
}

View File

@@ -6,6 +6,7 @@ import { DelonACLModule } from '@delon/acl';
import { DelonFormModule } from '@delon/form'; import { DelonFormModule } from '@delon/form';
import { AlainThemeModule } from '@delon/theme'; import { AlainThemeModule } from '@delon/theme';
import { KeyValueInputComponent } from './components/key-value-input/key-value-input.component';
import { TimezonePipe } from './pipe/timezone.pipe'; import { TimezonePipe } from './pipe/timezone.pipe';
import { SHARED_DELON_MODULES } from './shared-delon.module'; import { SHARED_DELON_MODULES } from './shared-delon.module';
import { SHARED_ZORRO_MODULES } from './shared-zorro.module'; import { SHARED_ZORRO_MODULES } from './shared-zorro.module';
@@ -18,7 +19,7 @@ const THIRDMODULES: Array<Type<void>> = [];
// #region your components & directives // #region your components & directives
const COMPONENTS: Array<Type<void>> = []; const COMPONENTS: Array<Type<void>> = [KeyValueInputComponent];
const DIRECTIVES: Array<Type<void>> = [TimezonePipe]; const DIRECTIVES: Array<Type<void>> = [TimezonePipe];
// #endregion // #endregion