mirror of
https://github.com/dromara/hertzbeat.git
synced 2026-09-17 18:19:02 +00:00
Compare commits
1097
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74cbfe0c74 | ||
|
|
ae5d30790c | ||
|
|
a6be462eba | ||
|
|
afa41621f7 | ||
|
|
92fce7edac | ||
|
|
e073bfee91 | ||
|
|
9e791ff6b8 | ||
|
|
6ce68ab94c | ||
|
|
38843e8e86 | ||
|
|
591ad4a101 | ||
|
|
f57972e389 | ||
|
|
4327e888da | ||
|
|
e2290bec92 | ||
|
|
0c1a810701 | ||
|
|
98275f3a61 | ||
|
|
f17844e3e8 | ||
|
|
0317b67975 | ||
|
|
262c6b31c7 | ||
|
|
d8dba67066 | ||
|
|
dd13b74a6f | ||
|
|
897d5d4220 | ||
|
|
4228605a3d | ||
|
|
e7131b1881 | ||
|
|
8a22797bca | ||
|
|
b9e569a1e0 | ||
|
|
1284b4b46b | ||
|
|
175c289e66 | ||
|
|
899e92882d | ||
|
|
d516b3a15d | ||
|
|
7678c3f139 | ||
|
|
3b49c0cb75 | ||
|
|
32d784e57e | ||
|
|
d8f8444275 | ||
|
|
41676c1f13 | ||
|
|
a3def497b4 | ||
|
|
381db85149 | ||
|
|
d0daa2eec5 | ||
|
|
9b5f0f7fce | ||
|
|
b3ef3d8a6f | ||
|
|
bd1924b016 | ||
|
|
1c781d5bc5 | ||
|
|
586aa53e84 | ||
|
|
c5f0389138 | ||
|
|
b93613c25e | ||
|
|
a8b0c1024d | ||
|
|
25a63ccb25 | ||
|
|
5cdfe545b5 | ||
|
+16 |
9be7295084 | ||
|
|
6905bcc986 | ||
|
|
1050032a13 | ||
|
|
9b223dd217 | ||
|
|
0a1b5961e9 | ||
|
|
a8a70b76d7 | ||
|
+1 |
05b63903b5 | ||
|
|
7d8b55dfb8 | ||
|
|
72a3e59ca6 | ||
|
|
3ab94a751b | ||
|
|
66049f189b | ||
|
|
69ec3f8751 | ||
|
|
feb43c5678 | ||
|
|
1fae67282d | ||
|
|
886f0b74f6 | ||
|
|
32e1a43aa5 | ||
|
|
dcd806802e | ||
|
|
5746cbbbd7 | ||
|
|
e23903fd0c | ||
|
|
780710f226 | ||
|
|
4607ede541 | ||
|
|
67ad4b04ef | ||
|
|
6432d689dd | ||
|
|
2eb856fffb | ||
|
|
56312831ca | ||
|
|
fe54d42bc0 | ||
|
|
2ea24c1b3a | ||
|
|
e0040e2ede | ||
|
|
0ac0fd4a8d | ||
|
|
0ef87bcc08 | ||
|
|
660bda6e14 | ||
|
|
242cf40135 | ||
|
|
7319c55430 | ||
|
|
5ea29f610b | ||
|
|
b74aff0500 | ||
|
|
d2e8f2f4aa | ||
|
|
28681a1f9e | ||
|
|
6814c7c357 | ||
|
|
f475540508 | ||
|
|
44660a5042 | ||
|
|
339386fd2b | ||
|
|
faa7cb71db | ||
|
|
014a376c30 | ||
|
|
377172e44a | ||
|
|
5b9e19cc30 | ||
|
|
0457ec7d16 | ||
|
|
2947ee218e | ||
|
|
4e841a9fb1 | ||
|
|
b7ecb9b2ec | ||
|
|
7e1873bc10 | ||
|
|
8eb60210a3 | ||
|
|
d608f732f7 | ||
|
|
2d9a2955ef | ||
|
|
cae4423f30 | ||
|
|
f0e53a9fb3 | ||
|
|
79da1a198b | ||
|
|
64cb804147 | ||
|
|
344cb25229 | ||
|
|
74d21a7acd | ||
|
|
f315bf9607 | ||
|
|
810d54bef5 | ||
|
|
48eafa1382 | ||
|
|
4b6c1e0f08 | ||
|
|
edfd857030 | ||
|
|
408a06d3eb | ||
|
|
fd3a2f0569 | ||
|
|
7d8ba72717 | ||
|
|
f936b50ce9 | ||
|
|
93f5da2d02 | ||
|
|
bc06c268ca | ||
|
|
9e6f2762c0 | ||
|
|
6ac8a42bd3 | ||
|
|
89647d96c7 | ||
|
|
0b0fac4a41 | ||
|
|
f59e15bbbe | ||
|
|
b7398b1bf9 | ||
|
|
0082e724bc | ||
|
|
57a2daf30d | ||
|
|
db90f29cd0 | ||
|
|
00fd8fa1da | ||
|
|
60100eb980 | ||
|
|
a6eb6be1ab | ||
|
|
5eff094af6 | ||
|
|
3b126f53d2 | ||
|
|
1a5b722994 | ||
|
|
5fe20dff05 | ||
|
|
04cfdae1e5 | ||
|
|
8776f98cf5 | ||
|
|
da484aef26 | ||
|
|
e8ebc65053 | ||
|
|
bd08de41d5 | ||
|
|
18f8a541f0 | ||
|
|
007ecd2db1 | ||
|
|
89aaad2401 | ||
|
|
17d836752e | ||
|
|
3949b73751 | ||
|
|
2731504278 | ||
|
|
babbfaea68 | ||
|
|
9f7b9994b8 | ||
|
|
d156c10aed | ||
|
|
9f3329c85f | ||
|
|
fd1c58f68d | ||
|
|
6d1aa503df | ||
|
|
a58ad35c8b | ||
|
|
64cd5f9658 | ||
|
|
a13dd89fd2 | ||
|
|
2f5cfa9d5d | ||
|
|
a9dc7bf7f3 | ||
|
|
c1492deaa0 | ||
|
|
2252b2a769 | ||
|
|
73f71503f5 | ||
|
|
2e224ce626 | ||
|
|
e5406d57c6 | ||
|
|
433a83303f | ||
|
|
f309d5eafd | ||
|
|
986103395b | ||
|
|
d569721e01 | ||
|
|
08cac3c6cc | ||
|
|
f80d066851 | ||
|
|
d6584093f5 | ||
|
|
fb03a57725 | ||
|
|
779272d65f | ||
|
|
c21a2952a5 | ||
|
|
b46d049158 | ||
|
|
dc52c88d88 | ||
|
|
df4e947c8a | ||
|
|
a4ec6c1f66 | ||
|
|
a8dd57d009 | ||
|
|
af3eabdd1c | ||
|
|
d6b300d3df | ||
|
|
b5789538e6 | ||
|
|
d6c5b2b8a2 | ||
|
|
3a04647c3c | ||
|
|
d2999c1aa6 | ||
|
|
b3dbc7092a | ||
|
|
edb3cb6b6a | ||
|
|
4dd78b2e98 | ||
|
|
2f91f56b7b | ||
|
|
be3dac488b | ||
|
|
553bdbaf9f | ||
|
|
40d3f1243b | ||
|
|
740e3f8385 | ||
|
|
47d089dff7 | ||
|
|
c6f1f07155 | ||
|
|
0ebd3499b7 | ||
|
|
c88d4a7b0e | ||
|
|
8bebae9db6 | ||
|
|
b6b1a48a5c | ||
|
|
63ed50ef45 | ||
|
|
e63715d1b0 | ||
|
|
edf3eac6d6 | ||
|
|
743f285c8d | ||
|
|
458433464c | ||
|
|
64740a4f85 | ||
|
|
b5a2bce261 | ||
|
|
5e351c854b | ||
|
|
0875bea046 | ||
|
|
9ad5f77816 | ||
|
|
998f9059bc | ||
|
|
a1439c3ec2 | ||
|
|
742b6f6755 | ||
|
|
5fdb34598c | ||
|
|
4f982d1ab0 | ||
|
|
4a28056d9f | ||
|
|
3f2fa71e22 | ||
|
|
08c81bf62f | ||
|
|
e16d6cb4ba | ||
|
|
a54d14e803 | ||
|
|
01ae281769 | ||
|
|
9cb10ff14d | ||
|
|
62f40d3ad1 | ||
|
|
57601ef989 | ||
|
|
0a05edbec5 | ||
|
|
fcef24fdbf | ||
|
|
e45c31abfb | ||
|
|
198c190ca7 | ||
|
|
392a59f9a9 | ||
|
|
c01b7f7bce | ||
|
|
0323b044b8 | ||
|
|
256302fdcf | ||
|
|
3f3330af41 | ||
|
|
33342baa3f | ||
|
|
563cbd7943 | ||
|
|
ddb1601290 | ||
|
|
8e9d3c09f3 | ||
|
|
ade04d2cf2 | ||
|
|
53555c88c7 | ||
|
|
03bf31269d | ||
|
|
5c329a8a66 | ||
|
|
dda9250243 | ||
|
|
65a97aed8f | ||
|
|
56528f4fc0 | ||
|
|
f218ba018a | ||
|
|
d6ce0c153a | ||
|
|
a1b23cacdc | ||
|
|
ed6214d0c1 | ||
|
|
9a3fc5911d | ||
|
|
d5d01459e7 | ||
|
|
d077c72211 | ||
|
|
11e3e4cc22 | ||
|
|
c24c6ab7aa | ||
|
|
a183176e83 | ||
|
|
a2e8739fec | ||
|
|
ebb6e3d4ff | ||
|
|
fc3f26368d | ||
|
|
7967cfabf0 | ||
|
|
199466cd59 | ||
|
|
8065b63057 | ||
|
|
88655fcf19 | ||
|
|
99b5460dd1 | ||
|
|
d681420c11 | ||
|
|
8618145e44 | ||
|
|
dd0045c6a4 | ||
|
|
84e0a604e7 | ||
|
|
ce253f7069 | ||
|
|
767b70f70b | ||
|
|
4aad08ca17 | ||
|
|
c176bb2988 | ||
|
|
3cb4fc71f9 | ||
|
|
c3f32132ca | ||
|
|
e1affb169f | ||
|
|
db27159947 | ||
|
|
63c187488f | ||
|
|
f3f66ada0d | ||
|
|
d5c934dc01 | ||
|
|
518eebb6b1 | ||
|
|
210454f969 | ||
|
|
a8db5e31a0 | ||
|
|
c01653aff3 | ||
|
|
480bd0e449 | ||
|
|
97faca6753 | ||
|
|
e271bd7417 | ||
|
|
7de4fd1cbf | ||
|
|
c2a7d0db35 | ||
|
|
93b2630bf3 | ||
|
|
23749e7fac | ||
|
|
d5eae40754 | ||
|
|
7ef79de1de | ||
|
|
8496771c8b | ||
|
|
f31c835181 | ||
|
|
552554b96d | ||
|
|
4adcfd2a4e | ||
|
|
e630beecb4 | ||
|
|
6c2b38e180 | ||
|
|
84986dd1bf | ||
|
|
af868e083a | ||
|
|
be3e2f3558 | ||
|
|
d32818e8ef | ||
|
|
10b283522c | ||
|
|
31ab2da41e | ||
|
|
a9436049e3 | ||
|
|
41c010511c | ||
|
|
b5281c1274 | ||
|
|
62cf3d1b2b | ||
|
|
dc8ae844c1 | ||
|
|
91a7593b87 | ||
|
|
4442a52adf | ||
|
|
60e3437e82 | ||
|
|
21d6ec2f1b | ||
|
|
3ed50f39cb | ||
|
|
dfba21f224 | ||
|
|
9fb3f39eb1 | ||
|
|
2430448089 | ||
|
|
993de3d504 | ||
|
|
955ecd2128 | ||
|
|
96e5402c09 | ||
|
|
0a6e8147e8 | ||
|
|
7779056983 | ||
|
|
ca3348adf2 | ||
|
|
dc822f50e8 | ||
|
|
896adb76a2 | ||
|
|
2a49fa5774 | ||
|
|
2259d51cae | ||
|
|
e9e49cfbc5 | ||
|
|
3eba906491 | ||
|
|
6645c5d4b2 | ||
|
|
88fb007ef0 | ||
|
|
4892ef101f | ||
|
|
d0b7e38537 | ||
|
|
096153b3a4 | ||
|
|
b313db36c6 | ||
|
|
47298033dc | ||
|
|
f05e91247a | ||
|
|
1592c93d25 | ||
|
|
46d609a95e | ||
|
|
f17fb02441 | ||
|
|
e6cc71ed56 | ||
|
|
e2a9160e59 | ||
|
|
7ebeee2c1f | ||
|
|
ccfc8dc8ce | ||
|
|
ca9db17241 | ||
|
|
7a40bdee12 | ||
|
|
5db20d33f4 | ||
|
|
896869c611 | ||
|
|
32ce88b2c0 | ||
|
|
2288d8e900 | ||
|
|
dbb81d44ff | ||
|
|
227666dd46 | ||
|
|
b14bc6569f | ||
|
|
7c7102da17 | ||
|
|
489eac8e11 | ||
|
|
900655680a | ||
|
|
e87ed449f2 | ||
|
|
ddf4759b34 | ||
|
|
a8ce48b50d | ||
|
|
a0f3890137 | ||
|
|
b02837b362 | ||
|
|
9f9cdcf1a9 | ||
|
|
326497939d | ||
|
|
d881aff90d | ||
|
|
78b0a428ff | ||
|
|
ba173acc09 | ||
|
|
9d842a241b | ||
|
|
c480d63833 | ||
|
|
ce479c6ba9 | ||
|
|
f683cab857 | ||
|
|
9c38bd5309 | ||
|
|
e7cd23a85d | ||
|
|
81ad7d51b6 | ||
|
|
9fa5b17fe4 | ||
|
|
9700161fdd | ||
|
|
f578df1deb | ||
|
|
eb58bb76f2 | ||
|
|
066d74629a | ||
|
|
9619638cff | ||
|
|
b91f849594 | ||
|
|
e1806f856f | ||
|
|
95654e49c5 | ||
|
|
9ceb6e6400 | ||
|
|
64cc15fabb | ||
|
|
27da850ca4 | ||
|
|
1ad52321ea | ||
|
|
ff037a6a3a | ||
|
|
433f09a4e3 | ||
|
|
706baffd5e | ||
|
|
0b6fb353f1 | ||
|
|
0385892674 | ||
|
|
04e2321175 | ||
|
|
8373f0a247 | ||
|
|
5ad03687b2 | ||
|
|
7127159063 | ||
|
|
51715797ef | ||
|
|
47a1b13781 | ||
|
|
d5fc492b98 | ||
|
|
bbc30cd3db | ||
|
|
6e50d1f3a7 | ||
|
|
b18412254e | ||
|
|
fc0ef7773c | ||
|
|
adf71441f8 | ||
|
|
e36842420f | ||
|
|
a2c1123b09 | ||
|
|
b4151a4e73 | ||
|
|
1c1683d9c6 | ||
|
|
46d4fc2dd9 | ||
|
|
ffa2487587 | ||
|
|
30c7b22c4c | ||
|
|
7a3e23ec59 | ||
|
|
da6556641a | ||
|
|
b008f16b67 | ||
|
|
b77222765c | ||
|
|
aeb4188f83 | ||
|
|
311cb131fd | ||
|
|
21558e9b4f | ||
|
|
8d1f306433 | ||
|
|
e98af9527e | ||
|
|
811a07553b | ||
|
|
9fd58ced4a | ||
|
|
a331c045ae | ||
|
|
63d3d18428 | ||
|
|
cec8d8b396 | ||
|
|
9d68d16a78 | ||
|
|
290474a715 | ||
|
|
0dbc7347b4 | ||
|
|
e2a2d5eaec | ||
|
|
356cca295d | ||
|
|
5e09c6d9ae | ||
|
|
54caa84fc1 | ||
|
|
d2a332f533 | ||
|
|
dc2efdf1c2 | ||
|
|
4defb733bf | ||
|
|
f8e85ec4f1 | ||
|
|
3468739504 | ||
|
|
5a92161cd9 | ||
|
|
4ce6ffd4ee | ||
|
|
9f34d201ae | ||
|
|
4b8ccf2fea | ||
|
|
adf27d2544 | ||
|
|
033b9b0706 | ||
|
|
01be0c7a1d | ||
|
|
0fdd76c0de | ||
|
|
1a38fcf776 | ||
|
|
268e111024 | ||
|
|
484dc5f70f | ||
|
|
b7bd9eb305 | ||
|
|
a5f874e682 | ||
|
|
fcf52b383a | ||
|
|
e93c3a9054 | ||
|
|
84f222b516 | ||
|
|
53bb58da5f | ||
|
|
3688974610 | ||
|
|
af35043663 | ||
|
|
84c28229b2 | ||
|
|
1a3e614209 | ||
|
|
3df0e56da9 | ||
|
|
b5b4771e1a | ||
|
|
b8c5ae4d40 | ||
|
|
207f2d958c | ||
|
|
398a40bba4 | ||
|
|
bb14681621 | ||
|
|
d3a080fbaf | ||
|
|
ce6f9feac6 | ||
|
|
7d18118bbd | ||
|
|
d5d2b6bc71 | ||
|
|
e11a4da2ac | ||
|
|
58360442ca | ||
|
|
4d76c0abf4 | ||
|
|
f761e18739 | ||
|
|
6ac073a54e | ||
|
|
65a8d19c5d | ||
|
|
060b4d875d | ||
|
|
6c88c1ba03 | ||
|
|
8c58a35476 | ||
|
|
2ab05a5669 | ||
|
|
f3bc7f55c5 | ||
|
|
7effbc1f0d | ||
|
|
05e42182d9 | ||
|
|
5e69f4dfd0 | ||
|
|
aeb0083394 | ||
|
|
02f635bdd3 | ||
|
|
7dbcaaeda8 | ||
|
|
4ddf0a9f3f | ||
|
|
f1b1e3f7d6 | ||
|
|
921b8e5713 | ||
|
|
4009525f81 | ||
|
|
e0c7b4e111 | ||
|
|
5e01482caa | ||
|
|
028e2fbcbe | ||
|
|
d00cc6dcf4 | ||
|
|
4b7fdd6985 | ||
|
|
e45ec431aa | ||
|
|
8fa52fbca1 | ||
|
|
132fd00ab1 | ||
|
|
12ba2faa19 | ||
|
|
2f3abfbad4 | ||
|
|
95739d9114 | ||
|
|
428ae1f7d5 | ||
|
|
6509b8bfc6 | ||
|
|
783bd8de89 | ||
|
|
d1115b44d1 | ||
|
|
68e37d613a | ||
|
|
7db0df11ba | ||
|
|
e244bdc5a7 | ||
|
|
0b7aa487f0 | ||
|
|
59d8764dfb | ||
|
|
8a38a34965 | ||
|
|
fd7e1b0e05 | ||
|
|
5441838888 | ||
|
|
56b1dcb502 | ||
|
|
efbaf7feac | ||
|
|
7003b8a150 | ||
|
|
aed524a88b | ||
|
|
9b20ab8434 | ||
|
|
5c39b98eaa | ||
|
|
43ac859939 | ||
|
|
b5c2eca819 | ||
|
|
cd57f51892 | ||
|
|
a33966de9f | ||
|
|
4ff9452926 | ||
|
|
67da9bdbba | ||
|
|
8bb4493b43 | ||
|
|
92edd53f8c | ||
|
|
d70ad75dcc | ||
|
|
2e76c12fe1 | ||
|
|
7e158eb778 | ||
|
|
db23c94882 | ||
|
|
e905f36de2 | ||
|
|
037957dbe6 | ||
|
|
01d18f7935 | ||
|
|
88e350de1c | ||
|
|
07d22a8712 | ||
|
|
7847fd78b7 | ||
|
|
f7676ac8ca | ||
|
|
dd1cc700d9 | ||
|
|
d8dabe251a | ||
|
|
f2e647d777 | ||
|
|
7bac0245a2 | ||
|
|
d6e5399121 | ||
|
|
00076fc704 | ||
|
|
4a3d61bd9e | ||
|
|
af59b487e4 | ||
|
|
4cd3c3c97f | ||
|
|
69a21ffaa1 | ||
|
|
8ab71e672f | ||
|
|
7625a0b508 | ||
|
|
dc963833e8 | ||
|
|
87d4379efb | ||
|
|
bffd7c1d4f | ||
|
|
ba21ef5cdb | ||
|
|
3bf237a4eb | ||
|
|
19e7f128a3 | ||
|
|
5c409d7512 | ||
|
|
6bf20ba21a | ||
|
|
8c9b1f3f92 | ||
|
|
b3eb0083df | ||
|
|
e51accb3e6 | ||
|
|
a9e219811a | ||
|
|
51bdb56592 | ||
|
|
3562e870d8 | ||
|
|
6f9ee2f4e6 | ||
|
|
917c11f298 | ||
|
|
bf8d5b79bb | ||
|
|
7178c444f2 | ||
|
|
bc2dc1d567 | ||
|
|
9714123c98 | ||
|
|
8ca2f70153 | ||
|
|
126268f7ec | ||
|
|
12d1f4990d | ||
|
|
4b21df556c | ||
|
|
893d91c4f0 | ||
|
|
4236c438be | ||
|
|
6a64752af0 | ||
|
|
c5cce27cb4 | ||
|
|
364de609bf | ||
|
|
5d81c32f9d | ||
|
|
6aff6ed053 | ||
|
|
5ba09cf5c1 | ||
|
|
a746e35dcf | ||
|
|
a27777adcb | ||
|
|
4b7dfb4c8e | ||
|
|
ceda4a65f9 | ||
|
|
d4f6fc1ae8 | ||
|
|
e999360da8 | ||
|
|
948fcb739a | ||
|
|
cdf060e9ac | ||
|
|
1280c61af1 | ||
|
|
86ad10ad7b | ||
|
|
8961cf70a3 | ||
|
|
f31909a30d | ||
|
|
2df7e1d269 | ||
|
|
c10ebf0b60 | ||
|
|
1fe70a60cc | ||
|
|
3ea4c0e7da | ||
|
|
8228938bd1 | ||
|
|
5acaf4844a | ||
|
|
fc8be06ad5 | ||
|
|
509a9c9651 | ||
|
|
754b18c9ba | ||
|
|
8cdc92ab56 | ||
|
|
f0cb2af031 | ||
|
|
bd937bf7b8 | ||
|
|
8852713e4d | ||
|
|
12f4ecce01 | ||
|
|
ca9934ec81 | ||
|
|
eaaf589083 | ||
|
|
be84a0abae | ||
|
|
9a00f5a776 | ||
|
|
fb4626dd07 | ||
|
|
f28996255a | ||
|
|
0b79222bd0 | ||
|
|
120f3a12fe | ||
|
|
b8b1885657 | ||
|
|
caf13c09bd | ||
|
|
dca3e351ed | ||
|
|
6298e7ae90 | ||
|
|
4b2c3756b1 | ||
|
|
d32ec249b4 | ||
|
|
611ddc22e3 | ||
|
|
4373ab23f3 | ||
|
|
bff75757db | ||
|
|
50dd262fbf | ||
|
|
dc9743b7a5 | ||
|
|
8dc0fdee27 | ||
|
|
1267fcae5a | ||
|
|
afe68ba4f8 | ||
|
|
4da00d445b | ||
|
|
e3d09ef427 | ||
|
|
bb0a22c42f | ||
|
|
291f7cb351 | ||
|
|
ba4f1e35f8 | ||
|
|
32f8cd5188 | ||
|
|
1f22a610ce | ||
|
|
fac3f5b927 | ||
|
|
efa2dc3969 | ||
|
|
a0995d26ec | ||
|
|
4f18c2c930 | ||
|
|
739e678dd5 | ||
|
|
2856c3fe65 | ||
|
|
bad49ce17e | ||
|
|
64c68a4593 | ||
|
|
cc47db793f | ||
|
|
108cc4db5a | ||
|
|
7e3d009536 | ||
|
|
5dabc7b7fd | ||
|
|
f2eb663e02 | ||
|
|
ebe7a6f8fe | ||
|
|
163a5a05ed | ||
|
|
ec195c1dbf | ||
|
|
5fa88ec577 | ||
|
|
1e4dd6793e | ||
|
|
a811b9f6cb | ||
|
|
a8790c856b | ||
|
|
b9fdfbd50e | ||
|
|
85b4a4ef0c | ||
|
|
febc911639 | ||
|
|
001e76db4b | ||
|
|
ca27605640 | ||
|
|
06ce10ec3a | ||
|
|
1c17c7cb44 | ||
|
|
a34ccdd769 | ||
|
|
f5a8ebeeca | ||
|
|
6c36d1d60a | ||
|
|
90c027d6cc | ||
|
|
c30295cf9c | ||
|
|
bb0e2ea41c | ||
|
|
16f52d1b6e | ||
|
|
ff3139e1de | ||
|
|
65639da9c4 | ||
|
|
af0dc23b45 | ||
|
|
4b81ae4abf | ||
|
|
d5fdccf1d2 | ||
|
|
4d9900b56b | ||
|
|
dab66d4f16 | ||
|
|
cd028d8464 | ||
|
|
c4c56c6f85 | ||
|
|
6a852b2b8d | ||
|
|
c153433165 | ||
|
|
27175ef3ef | ||
|
|
c507f952de | ||
|
|
b00a50a469 | ||
|
|
98354f267e | ||
|
|
9080ccd2ba | ||
|
|
bd3420c4a7 | ||
|
|
7506429fc4 | ||
|
|
e9bfee6e7e | ||
|
|
b8a2e865cd | ||
|
|
83c5a3a08a | ||
|
|
4b7178af02 | ||
|
|
1841ed59d8 | ||
|
|
4080f5f068 | ||
|
|
25ee822c5f | ||
|
|
969c4a95c5 | ||
|
|
a15a106a46 | ||
|
|
74ed750860 | ||
|
|
ef377912ef | ||
|
|
cb11f11ace | ||
|
|
589b0d915d | ||
|
|
50a18f2813 | ||
|
|
86ca715a64 | ||
|
|
7b886fe72d | ||
|
|
9024b2aca2 | ||
|
|
5cd9590907 | ||
|
|
8e38b8ba9b | ||
|
|
47e632a088 | ||
|
|
4a691777aa | ||
|
|
ec92f4ee97 | ||
|
|
714f216470 | ||
|
|
16cb8f681c | ||
|
|
3ec150c586 | ||
|
|
31fcb0d839 | ||
|
|
3e1d87b59f | ||
|
|
2076693a16 | ||
|
|
7bb049f2df | ||
|
|
19955fa59a | ||
|
|
049c0937bc | ||
|
|
2763ee4dc4 | ||
|
|
497fc915c3 | ||
|
|
e945c42632 | ||
|
|
59398bd7c6 | ||
|
|
d49cb37c6f | ||
|
|
89be682a88 | ||
|
|
0d73ac6fc4 | ||
|
|
17636ed582 | ||
|
|
9cb50b7190 | ||
|
|
0e49e100d8 | ||
|
|
24678c2ca0 | ||
|
|
6074c08e1a | ||
|
|
3eb6fb35f4 | ||
|
|
466ac37e66 | ||
|
|
a5bc6327f9 | ||
|
|
2e9021a12d | ||
|
|
1dd08b8b19 | ||
|
|
6117396377 | ||
|
|
d71b9aa048 | ||
|
|
525980f919 | ||
|
|
6e430bcab6 | ||
|
|
12a53c69da | ||
|
|
1799e00ec9 | ||
|
|
5d416e855b | ||
|
|
5eef247a5b | ||
|
|
77f725cf42 | ||
|
|
b7969e78c9 | ||
|
|
2f4343c453 | ||
|
|
857e4118f0 | ||
|
|
c3ffad76ca | ||
|
|
102c72bcc6 | ||
|
|
88aeb9fc21 | ||
|
|
20f89f3b3b | ||
|
|
7facbe827c | ||
|
|
e3a1b8345f | ||
|
|
0f0f00c700 | ||
|
|
51699e34cd | ||
|
|
343afd517d | ||
|
|
b602d8ceb6 | ||
|
|
bad28bccdb | ||
|
|
eea68d2a27 | ||
|
|
fff291da82 | ||
|
|
97aada9956 | ||
|
|
d056ce2aa6 | ||
|
|
c2d92eb57d | ||
|
|
4dceb42dc7 | ||
|
|
1fac6ab2fa | ||
|
|
235df8bca1 | ||
|
|
603bc3222a | ||
|
|
a1d73a292c | ||
|
|
bdfffeec4c | ||
|
|
bc47768bfb | ||
|
|
98b03b62c7 | ||
|
|
ceab3023b1 | ||
|
|
a49fa3d91b | ||
|
|
5d87275206 | ||
|
|
a2bd61947b | ||
|
|
7ac21d3171 | ||
|
|
4fdc048287 | ||
|
|
e10e4b2d8d | ||
|
|
27d66a11c1 | ||
|
|
19c7d84e53 | ||
|
|
031e83d701 | ||
|
|
5b50307e8c | ||
|
|
4f7ba7355f | ||
|
|
4733ac2eba | ||
|
|
e157d82a18 | ||
|
|
f1a123ee76 | ||
|
|
584b70a726 | ||
|
|
9d62270131 | ||
|
|
53a506569a | ||
|
|
0ec6a3b6ba | ||
|
|
c8560e6b16 | ||
|
|
361e573d8e | ||
|
|
c12b4c0d84 | ||
|
|
9b065f7ec0 | ||
|
|
ffa81a68f7 | ||
|
|
ed21cc3a94 | ||
|
|
35616d2763 | ||
|
|
fbb74199c9 | ||
|
|
3a2350b86d | ||
|
|
b39602f15d | ||
|
|
86c7c2dadf | ||
|
|
ddfa60e597 | ||
|
|
e34b64062e | ||
|
|
0634e7bb2e | ||
|
|
0920910738 | ||
|
|
8ae4d0e2d3 | ||
|
|
1c5b3f9c3f | ||
|
|
a55a098983 | ||
|
|
a8c1c48390 | ||
|
|
9f7be0292a | ||
|
|
3250bc714d | ||
|
|
3d3d881722 | ||
|
|
3c01298e86 | ||
|
|
c650972f3d | ||
|
|
df4272a2f8 | ||
|
|
23d16ae921 | ||
|
|
aeac1ffed4 | ||
|
|
bac607f6dd | ||
|
|
8faa13e7b4 | ||
|
|
3113487192 | ||
|
|
7a7d4792cd | ||
|
|
ec8cc517d1 | ||
|
|
12fee7fb01 | ||
|
|
339a33a0bd | ||
|
|
e3a0fb4537 | ||
|
|
9b328ce34c | ||
|
|
e8dc6482ff | ||
|
|
c4fa738d65 | ||
|
|
6ac092dcd5 | ||
|
|
fe403ed880 | ||
|
|
d6b19691fb | ||
|
|
c2c00d44c7 | ||
|
|
e9da2d2925 | ||
|
|
58e6e1a2a7 | ||
|
|
0c2cc59b55 | ||
|
|
111f0f6b99 | ||
|
|
e560502d8e | ||
|
|
67983c2702 | ||
|
|
033240341b | ||
|
|
35a8ec4275 | ||
|
|
9ac45ab343 | ||
|
|
4b5a97044c | ||
|
|
477af0959f | ||
|
|
a5b8f59242 | ||
|
|
98b8d95b92 | ||
|
|
c0ef4a436e | ||
|
|
254421bc46 | ||
|
|
deb827a572 | ||
|
|
7eef0c21f1 | ||
|
|
58920978c3 | ||
|
|
a4c6a6045f | ||
|
|
6e47da758e | ||
|
|
ae03111a75 | ||
|
|
69b8db7048 | ||
|
|
de657f9144 | ||
|
|
822825b614 | ||
|
|
7672502c3d | ||
|
|
598ed2ea16 | ||
|
|
aee969a18a | ||
|
|
b0438da537 | ||
|
|
c85e182119 | ||
|
|
7446d3ebbf | ||
|
|
f6e8e64e23 | ||
|
|
2a3f4dfa80 | ||
|
|
3d99835487 | ||
|
|
3a7d5829ad | ||
|
|
51b1a9bdc7 | ||
|
|
181ca5aa5e | ||
|
|
a5c444a49a | ||
|
|
2784c8fa6b | ||
|
|
ae4a5b1c63 | ||
|
|
c7f4390638 | ||
|
|
c998d3b850 | ||
|
|
861e9d377f | ||
|
|
c1252fbe2b | ||
|
|
dce9a4b15e | ||
|
|
15768d8077 | ||
|
|
2824542037 | ||
|
|
f9bce29523 | ||
|
|
6b7dbd53f4 | ||
|
|
f5e88db841 | ||
|
|
82219d14df | ||
|
|
05bce69b84 | ||
|
|
bc49280c3c | ||
|
|
90035cac5d | ||
|
|
3487e4a16b | ||
|
|
974b27e878 | ||
|
|
22459c5b6f | ||
|
|
64e156be2a | ||
|
|
a9827bbcef | ||
|
|
0ed1355d76 | ||
|
|
0b81717a9e | ||
|
|
86ca62c67a | ||
|
|
7488dfadc9 | ||
|
|
50fe51bd95 | ||
|
|
8233a62aea | ||
|
|
7367b7315b | ||
|
|
9e305f7a59 | ||
|
|
3d9be220e6 | ||
|
|
659346ddd7 | ||
|
|
7f0b340663 | ||
|
|
a427099db7 | ||
|
|
d1d28e6763 | ||
|
|
ba70b2f269 | ||
|
|
2352ceef3d | ||
|
|
927625a79f | ||
|
|
16a6620ff6 | ||
|
|
a1d016e5d5 | ||
|
|
40aa5a4d73 | ||
|
|
aef7ac23c4 | ||
|
|
348b50c4c6 | ||
|
|
88538890b4 | ||
|
|
fa4a3fd9e1 | ||
|
|
3a24edb309 | ||
|
|
e142a498e2 | ||
|
|
f4685aa75a | ||
|
|
f038ec5326 | ||
|
|
06186c5ccc | ||
|
|
6c931956fa | ||
|
|
809f679375 | ||
|
|
b31d8b1380 | ||
|
|
1bba56bfbc | ||
|
|
357068cc8f | ||
|
|
8a88607be9 | ||
|
|
15e9bd0656 | ||
|
|
aa58f0af7f | ||
|
|
115f7b40c5 | ||
|
|
d2c19cbf95 | ||
|
|
173733784b | ||
|
|
df4115a64c | ||
|
|
8d34de4422 | ||
|
|
a482d24770 | ||
|
|
aee8481204 | ||
|
|
497338e00f | ||
|
|
e5236259ff | ||
|
|
712c54186e | ||
|
|
7aa3be8f7d | ||
|
|
f84e1fc5bf | ||
|
|
a8c1921fa3 | ||
|
|
43afb0bc63 | ||
|
|
ee9bdf6773 | ||
|
|
229b4158cc | ||
|
|
3d49b288bb | ||
|
|
e0caceda92 | ||
|
|
0b73e0900b | ||
|
|
1a09611ad6 | ||
|
|
be0de49646 | ||
|
|
061423d301 | ||
|
|
6421fef548 | ||
|
|
70af0669c6 | ||
|
|
55b3dd7f0c | ||
|
|
91111e5555 | ||
|
|
07913e7d51 | ||
|
|
38d466b7f8 | ||
|
|
9770c9321e | ||
|
|
7e5283ea0d | ||
|
|
8f659f0fd1 | ||
|
|
7a4ee880c3 | ||
|
|
3b323e1882 | ||
|
|
acc617f9bd | ||
|
|
5585993503 | ||
|
|
fd0e216818 | ||
|
|
999b37ec4f | ||
|
|
823430bca9 | ||
|
|
9a7e0411a1 | ||
|
|
3a2e0b3dd2 | ||
|
|
25d27880aa | ||
|
|
8a11dacdba | ||
|
|
467c595802 | ||
|
|
c70cc5d9b7 | ||
|
|
8f9143001f | ||
|
|
3e50b96322 | ||
|
|
9cf0e6f4ee | ||
|
|
d47d3be6b4 | ||
|
|
6cabe3ca0c | ||
|
|
874f7bdab3 | ||
|
|
f90d1e6242 | ||
|
|
afcb0c19fb | ||
|
|
0e7323fc04 | ||
|
|
feb60a7da9 | ||
|
|
2e7616cd56 | ||
|
|
2d53abdaab | ||
|
|
9fed2c8a36 | ||
|
|
a4db859eee | ||
|
|
9a3a2f0a54 | ||
|
|
da05b6a151 | ||
|
|
78621f2f14 | ||
|
|
a618ff00ea | ||
|
|
044c6d4bad | ||
|
|
53787cc215 | ||
|
|
d884278fed | ||
|
|
21a7788522 | ||
|
|
c7d192996a | ||
|
|
78ce1cdc1f | ||
|
|
12230b47ca | ||
|
|
753ea44d2b | ||
|
|
c3faea07c2 | ||
|
|
4149a027a7 | ||
|
|
65d69eb191 | ||
|
|
5e2e78fe95 | ||
|
|
e992da78cd | ||
|
|
2fd0090e83 | ||
|
|
a86fc85ffc | ||
|
|
f3cecea307 | ||
|
|
cbf6536923 | ||
|
|
336eaffd27 | ||
|
|
b9f8b7de72 | ||
|
|
5b44a95298 | ||
|
|
97e5435296 | ||
|
|
9b31fe785f | ||
|
|
f6fcd52a50 | ||
|
|
19a59004de | ||
|
|
f9b04ceb0c | ||
|
|
7ee227196d | ||
|
|
20f3b01251 | ||
|
|
9260bca057 | ||
|
|
6cdffbefac | ||
|
|
0699e89346 | ||
|
|
6222c51f41 | ||
|
|
ccbc7c698e | ||
|
|
b5c7705f4a | ||
|
|
eeba46af9f | ||
|
|
b4d0e77c99 | ||
|
|
1cb293ac03 | ||
|
|
2ffdda8f26 | ||
|
|
4f1c62fa3a | ||
|
|
bfe68d8a06 | ||
|
|
215271716d | ||
|
|
fafc6a8212 | ||
|
|
e860e63f62 | ||
|
|
702446190d | ||
|
|
7251fb4185 | ||
|
|
12bb21cb8d | ||
|
|
a7f30fc1f6 | ||
|
|
2cc779ce1c | ||
|
|
27c394d9f7 | ||
|
|
b25f1834e8 | ||
|
|
c878ac8fad | ||
|
|
0ebcffa408 | ||
|
|
5ef96061c9 | ||
|
|
c06f53c926 | ||
|
|
51765d8f9a | ||
|
|
1fe37d3789 | ||
|
|
baf2d5f536 | ||
|
|
5757d7427d | ||
|
|
cbb4b8c736 | ||
|
|
dbac0dfe63 | ||
|
|
8c0b39f69b | ||
|
|
a54d8cdd11 | ||
|
|
ec1c41cf68 | ||
|
|
93635a0482 | ||
|
|
59266ccb8a | ||
|
|
bc4b399a21 | ||
|
|
87756f16d9 | ||
|
|
e1cda5066e | ||
|
|
fa0e9ef50a | ||
|
|
a10c2bfbd8 | ||
|
|
33877615c5 | ||
|
|
13411105ed | ||
|
|
e2faa44f18 | ||
|
|
657de4f5b6 | ||
|
|
dfa6895a7e | ||
|
|
a9de486744 | ||
|
|
31d8712299 | ||
|
|
c4a6e36977 | ||
|
|
41c3bf50ee | ||
|
|
957cce93d9 | ||
|
|
9b7b00e09c | ||
|
|
b3976295f5 | ||
|
|
1be2631af7 | ||
|
|
5f5f4d0346 | ||
|
|
95c30f54cd | ||
|
|
855abf6ada | ||
|
|
bdf82751a5 | ||
|
|
f49e20535c | ||
|
|
a32757ea7a | ||
|
|
bd52e0527e | ||
|
|
1bac42b094 | ||
|
|
a71c61a0d8 | ||
|
|
56db540721 | ||
|
|
bc104dfe9f | ||
|
|
fc6ea4a2e0 | ||
|
|
55c48d167c | ||
|
|
393bb95878 | ||
|
|
915fcbf1d9 | ||
|
|
26b982a59b | ||
|
|
3186aa5eb6 | ||
|
|
a6ac326581 | ||
|
|
53b796ff57 | ||
|
|
b7217b5d48 | ||
|
|
b8f2bf30fd | ||
|
|
bc4ddefc76 | ||
|
|
2fcd100fec | ||
|
|
365de4657e | ||
|
|
d4e6317a30 | ||
|
|
cc19831f7c | ||
|
|
f10e5dbd06 | ||
|
|
af1f59e009 | ||
|
|
009a512ba0 | ||
|
|
dc381ea338 | ||
|
|
3ed52c54af | ||
|
|
c5808a0430 | ||
|
|
ed390a50c9 | ||
|
|
cfe93bf19d |
+808
-30
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@
|
||||
#
|
||||
|
||||
github:
|
||||
description: Apache HertzBeat(incubating) is a real-time monitoring system with agentless, performance cluster, prometheus-compatible, custom monitoring and status page building capabilities.
|
||||
description: Real-time observability system with agentless, performance cluster, prometheus-compatible, custom monitoring and status page building capabilities.
|
||||
homepage: https://hertzbeat.apache.org/
|
||||
labels:
|
||||
- monitoring
|
||||
@@ -52,6 +52,9 @@ github:
|
||||
required_pull_request_reviews:
|
||||
dismiss_stale_reviews: true
|
||||
required_approving_review_count: 1
|
||||
# enable GitHub Dependabot to create PRs for security alerts but not for every dependency update
|
||||
dependabot_alerts: true
|
||||
dependabot_updates: false
|
||||
notifications:
|
||||
commits: notifications@hertzbeat.apache.org
|
||||
issues: notifications@hertzbeat.apache.org
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "Hertzbeat DevContainer",
|
||||
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/java:1": {
|
||||
"version": "17",
|
||||
"installMaven": "true",
|
||||
"installGradle": "false"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version": "20"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/git-lfs:1.1.0": {}
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {},
|
||||
"extensions": [
|
||||
"vscjava.vscode-java-pack",
|
||||
"editorconfig.editorconfig",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"stylelint.vscode-stylelint",
|
||||
"DavidAnson.vscode-markdownlint",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"cweijan.vscode-database-client2",
|
||||
"GitHub.vscode-pull-request-github"
|
||||
]
|
||||
}
|
||||
},
|
||||
"portsAttributes": {
|
||||
"4200": {
|
||||
"label": "Hertzbeat Web",
|
||||
"onAutoForward": "notify"
|
||||
}
|
||||
},
|
||||
"postCreateCommand": "java -version"
|
||||
}
|
||||
@@ -17,7 +17,9 @@
|
||||
|
||||
/home export-ignore
|
||||
/hip export-ignore
|
||||
/template-marketplace export-ignore
|
||||
/.github export-ignore
|
||||
/.idea export-ignore
|
||||
/.devcontainer export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
|
||||
@@ -20,6 +20,3 @@ contact_links:
|
||||
- name: Community Support
|
||||
url: https://github.com/apache/hertzbeat/discussions
|
||||
about: Please ask and answer questions here.
|
||||
- name: Security Bug Bounty
|
||||
url: https://github.com/apache/hertzbeat/issues
|
||||
about: Please report security vulnerabilities here.
|
||||
|
||||
@@ -53,4 +53,5 @@ body:
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
value: "Please read the [Contribution Guideline](https://hertzbeat.apache.org/docs/others/contributing) before submitting the PR"
|
||||
attributes:
|
||||
value: "Please read the [Contribution Guideline](https://hertzbeat.apache.org/docs/community/contribution) before submitting the PR"
|
||||
|
||||
@@ -39,4 +39,4 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please read the [Contribution Guideline](https://hertzbeat.com/docs/others/contributing/) before submitting the PR
|
||||
Please read the [Contribution Guideline](https://hertzbeat.apache.org/docs/community/contribution) before submitting the PR
|
||||
|
||||
@@ -30,5 +30,5 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please read the [Contribution Guideline](https://hertzbeat.apache.org/docs/others/contributing/) before submitting the PR
|
||||
Please read the [Contribution Guideline](https://hertzbeat.apache.org/docs/community/contribution) before submitting the PR
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ name: Backend CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, dev ]
|
||||
branches: [ master, dev, action* ]
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'home/**'
|
||||
@@ -39,29 +39,45 @@ on:
|
||||
- 'material/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
backend-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 17
|
||||
- name: Build with Maven
|
||||
run: mvn clean -B package -Prelease --file pom.xml
|
||||
- name: Build Image
|
||||
env:
|
||||
IMAGE_PUSH: false
|
||||
IMAGE_LOAD: true
|
||||
IMAGE_PLATFORM: linux/amd64
|
||||
run: |
|
||||
docker buildx create --use --name myBuilder --driver docker-container
|
||||
docker buildx use myBuilder
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./script/ci/github-actions/setup-deps
|
||||
|
||||
./script/docker/server/build.sh
|
||||
- name: Run E2E
|
||||
run: |
|
||||
sudo curl -L https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
|
||||
sudo chmod u+x /usr/local/bin/docker-compose
|
||||
- name: Build with Maven
|
||||
run: mvnd clean -B package -Prelease -Dmaven.test.skip=false --file pom.xml
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v4.0.1
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
verbose: true
|
||||
|
||||
- name: Build Image
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./dist
|
||||
file: ./script/docker/server/Dockerfile
|
||||
push: false
|
||||
tags: apache/hertzbeat:test
|
||||
|
||||
- name: Run E2E
|
||||
run: |
|
||||
sudo curl -L https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
|
||||
sudo chmod u+x /usr/local/bin/docker-compose
|
||||
|
||||
cd e2e
|
||||
sudo docker-compose version
|
||||
sudo docker-compose up --exit-code-from testing --remove-orphans
|
||||
|
||||
# upload application logs
|
||||
- name: Upload logs & API test reports
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: hz-logs-${{ github.run_id }}
|
||||
path: |
|
||||
e2e/logs/
|
||||
e2e/report/
|
||||
|
||||
cd e2e && ./start.sh
|
||||
|
||||
@@ -31,6 +31,8 @@ name: "CodeQL"
|
||||
on:
|
||||
schedule:
|
||||
- cron: '21 13 * * 4'
|
||||
push:
|
||||
branches: [ action* ]
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
@@ -47,7 +49,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
@@ -19,7 +19,7 @@ name: DOC CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, dev ]
|
||||
branches: [ master, dev, action* ]
|
||||
paths:
|
||||
- 'home/**'
|
||||
pull_request:
|
||||
@@ -28,91 +28,35 @@ on:
|
||||
- 'home/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
docs-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check filename in home/blog
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.8'
|
||||
|
||||
- name: Check Markdown
|
||||
uses: DavidAnson/markdownlint-cli2-action@v16
|
||||
with:
|
||||
globs: './home/**/*.md'
|
||||
|
||||
- name: Check filenames
|
||||
run: python ./script/ci/docs/check_file_name.py ./script/ci/docs/check_file_name.json
|
||||
|
||||
- name: Dead Link Check
|
||||
run: |
|
||||
TARGET_DIR="./home/blog"
|
||||
invalid_files=()
|
||||
while IFS= read -r -d '' file; do
|
||||
filename=$(basename "$file")
|
||||
if [[ ! "$filename" =~ ^[_a-z0-9-]+(\.[_a-z0-9-]+)*$ ]]; then
|
||||
invalid_files+=("$file")
|
||||
fi
|
||||
done < <(find "$TARGET_DIR" -type f -print0)
|
||||
if [ ${#invalid_files[@]} -ne 0 ]; then
|
||||
echo "Error: The following files have invalid names:(File name should only contain lowercase letters, numbers, and hyphens.)"
|
||||
for invalid_file in "${invalid_files[@]}"; do
|
||||
echo "$invalid_file"
|
||||
done
|
||||
exit 1
|
||||
else
|
||||
echo "All file names are valid."
|
||||
fi
|
||||
- name: Check filename in home/docs
|
||||
run: |
|
||||
TARGET_DIR="./home/docs"
|
||||
invalid_files=()
|
||||
while IFS= read -r -d '' file; do
|
||||
filename=$(basename "$file")
|
||||
if [[ ! "$filename" =~ ^[_a-z0-9-]+(\.[_a-z0-9-]+)*$ ]]; then
|
||||
invalid_files+=("$file")
|
||||
fi
|
||||
done < <(find "$TARGET_DIR" -type f -print0)
|
||||
if [ ${#invalid_files[@]} -ne 0 ]; then
|
||||
echo "Error: The following files have invalid names:(File name should only contain lowercase letters, numbers, and hyphens.)"
|
||||
for invalid_file in "${invalid_files[@]}"; do
|
||||
echo "$invalid_file"
|
||||
done
|
||||
exit 1
|
||||
else
|
||||
echo "All file names are valid."
|
||||
fi
|
||||
- name: Check filename in /zh-cn/docusaurus-plugin-content-blog
|
||||
run: |
|
||||
TARGET_DIR="./home/i18n/zh-cn/docusaurus-plugin-content-blog"
|
||||
invalid_files=()
|
||||
while IFS= read -r -d '' file; do
|
||||
filename=$(basename "$file")
|
||||
if [[ ! "$filename" =~ ^[_a-z0-9-]+(\.[_a-z0-9-]+)*$ ]]; then
|
||||
invalid_files+=("$file")
|
||||
fi
|
||||
done < <(find "$TARGET_DIR" -type f -print0)
|
||||
if [ ${#invalid_files[@]} -ne 0 ]; then
|
||||
echo "Error: The following files have invalid names:(File name should only contain lowercase letters, numbers, and hyphens.)"
|
||||
for invalid_file in "${invalid_files[@]}"; do
|
||||
echo "$invalid_file"
|
||||
done
|
||||
exit 1
|
||||
else
|
||||
echo "All file names are valid."
|
||||
fi
|
||||
- name: Check filename in /home/i18n/zh-cn/docusaurus-plugin-content-docs/current
|
||||
run: |
|
||||
TARGET_DIR="./home/i18n/zh-cn/docusaurus-plugin-content-docs/current"
|
||||
invalid_files=()
|
||||
while IFS= read -r -d '' file; do
|
||||
filename=$(basename "$file")
|
||||
if [[ ! "$filename" =~ ^[_a-z0-9-]+(\.[_a-z0-9-]+)*$ ]]; then
|
||||
invalid_files+=("$file")
|
||||
fi
|
||||
done < <(find "$TARGET_DIR" -type f -print0)
|
||||
if [ ${#invalid_files[@]} -ne 0 ]; then
|
||||
echo "Error: The following files have invalid names:(File name should only contain lowercase letters, numbers, and hyphens.)"
|
||||
for invalid_file in "${invalid_files[@]}"; do
|
||||
echo "$invalid_file"
|
||||
done
|
||||
exit 1
|
||||
else
|
||||
echo "All file names are valid."
|
||||
fi
|
||||
sudo npm install -g markdown-link-check@3.8.7
|
||||
find ./home -name "*.md" > all_md_files.txt
|
||||
grep -vFf ./script/ci/exclude_files.txt all_md_files.txt > to_check.txt
|
||||
xargs -P 8 -a to_check.txt -I{} markdown-link-check -c ./script/ci/link_check.json -q "{}"
|
||||
|
||||
- name: NPM INSTALL
|
||||
working-directory: home
|
||||
run: npm install
|
||||
|
||||
- name: NPM BUILD
|
||||
working-directory: home
|
||||
run: npm run build
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
|
||||
name: 'HertzBeat Doc Pdf Builder'
|
||||
on:
|
||||
page_build
|
||||
push:
|
||||
branches: [ page_build* ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -39,7 +40,7 @@ jobs:
|
||||
run: npx docusaurus-prince-pdf -u https://hertzbeat.apache.org/docs --output docs-en.pdf
|
||||
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: docs-cn-pdf
|
||||
path: docs-cn.pdf
|
||||
@@ -47,10 +48,9 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: docs-en-pdf
|
||||
path: docs-en.pdf
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ name: Frontend CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, dev ]
|
||||
branches: [ master, dev, action* ]
|
||||
paths:
|
||||
- 'web-app/**'
|
||||
pull_request:
|
||||
@@ -34,10 +34,13 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Yarn Install
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
- name: Install
|
||||
working-directory: web-app
|
||||
run: yarn
|
||||
run: pnpm install
|
||||
- name: EsLint Test
|
||||
working-directory: web-app
|
||||
run: yarn lint:ts
|
||||
run: pnpm lint:ts
|
||||
|
||||
@@ -22,7 +22,7 @@ name: License Checker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, dev ]
|
||||
branches: [ master, dev, action* ]
|
||||
pull_request:
|
||||
branches: [ master, dev ]
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Check license header
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
# 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.
|
||||
|
||||
name: MCP Bash Server CI
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'mcp-servers/mcp-bash-server/**'
|
||||
- '.github/workflows/mcp-bashserver-test.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'mcp-servers/mcp-bash-server/**'
|
||||
- '.github/workflows/mcp-bashserver-test.yml'
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_VERSION: 1.88.0
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test MCP Bash Server
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
profile: [dev, release]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ env.RUST_VERSION }}
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cargo-registry-
|
||||
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: target/
|
||||
key: ${{ runner.os }}-cargo-build-${{ matrix.profile }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cargo-build-${{ matrix.profile }}-
|
||||
${{ runner.os }}-cargo-build-
|
||||
|
||||
- name: Check code format
|
||||
run: cargo fmt --manifest-path ./mcp-servers/mcp-bash-server/Cargo.toml --check
|
||||
|
||||
- name: Run cargo check
|
||||
run: |
|
||||
if [ "${{ matrix.profile }}" = "release" ]; then
|
||||
cargo check --release --manifest-path ./mcp-servers/mcp-bash-server/Cargo.toml
|
||||
else
|
||||
cargo check --manifest-path ./mcp-servers/mcp-bash-server/Cargo.toml
|
||||
fi
|
||||
|
||||
- name: Run clippy
|
||||
run: |
|
||||
if [ "${{ matrix.profile }}" = "release" ]; then
|
||||
cargo clippy --release --manifest-path ./mcp-servers/mcp-bash-server/Cargo.toml -- -D warnings
|
||||
else
|
||||
cargo clippy --manifest-path ./mcp-servers/mcp-bash-server/Cargo.toml -- -D warnings
|
||||
fi
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
if [ "${{ matrix.profile }}" = "release" ]; then
|
||||
cargo test --release --manifest-path ./mcp-servers/mcp-bash-server/Cargo.toml
|
||||
else
|
||||
cargo test --manifest-path ./mcp-servers/mcp-bash-server/Cargo.toml
|
||||
fi
|
||||
@@ -0,0 +1,53 @@
|
||||
# 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.
|
||||
|
||||
# This workflow will build a Java project with Maven
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
name: Monitor E2E Test
|
||||
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# trigger at 01:00 everyday
|
||||
- cron: '0 1 * * *'
|
||||
push:
|
||||
branches: [ action* ]
|
||||
jobs:
|
||||
check-license-header:
|
||||
name: Monitor E2E Test
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./script/ci/github-actions/setup-deps
|
||||
- name: Build with Maven
|
||||
run: mvn clean -B package -Prelease -Dmaven.test.skip=true --file pom.xml
|
||||
- name: Build Image
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./dist
|
||||
file: ./script/docker/server/Dockerfile
|
||||
push: false
|
||||
tags: apache/hertzbeat:test
|
||||
- name: Run K8s Monitor E2E Test
|
||||
run: |
|
||||
sudo curl -L https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
|
||||
sudo chmod u+x /usr/local/bin/docker-compose
|
||||
cd e2e/k8s
|
||||
sudo docker-compose version
|
||||
sudo docker-compose up --exit-code-from testing --remove-orphans
|
||||
@@ -0,0 +1,75 @@
|
||||
# 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.
|
||||
|
||||
name: Nightly CI
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# trigger at 00:00 everyday
|
||||
- cron: '0 0 * * *'
|
||||
push:
|
||||
branches: [ action* ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'apache/hertzbeat' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./script/ci/github-actions/setup-deps
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Build the Frontend
|
||||
run: |
|
||||
cd web-app
|
||||
pnpm install
|
||||
pnpm package
|
||||
|
||||
- name: Build the Backend
|
||||
run: |
|
||||
mvn clean install
|
||||
mvn clean package -Prelease -DskipTests
|
||||
cd hertzbeat-collector
|
||||
mvn clean package -Pcluster -DskipTests
|
||||
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USER }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and Push Server
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./dist
|
||||
file: ./script/docker/server/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: apache/hertzbeat:nightly
|
||||
|
||||
- name: Build and Push Collector
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./dist
|
||||
file: ./script/docker/collector/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: apache/hertzbeat-collector:nightly
|
||||
+5
-3
@@ -6,13 +6,13 @@ Thumbs.db
|
||||
build/
|
||||
out/
|
||||
micronaut-cli.yml
|
||||
.mvn/
|
||||
mvnw
|
||||
mvnw.bat
|
||||
*.log
|
||||
package-lock.json
|
||||
*.zip
|
||||
jdk/
|
||||
data/
|
||||
|
||||
!/e2e/**/data/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
@@ -54,3 +54,5 @@ node_modules
|
||||
application-dev.yml
|
||||
application-mysql.yml
|
||||
application-pg.yml
|
||||
/package.json
|
||||
/yarn.lock
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
[submodule "script/helm/hertzbeat-helm-chart"]
|
||||
path = script/helm/hertzbeat-helm-chart
|
||||
url = git@github.com:apache/hertzbeat-helm-chart.git
|
||||
branch = main
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
FROM gitpod/workspace-full
|
||||
|
||||
USER gitpod
|
||||
|
||||
RUN bash -c ". /home/gitpod/.sdkman/bin/sdkman-init.sh && \
|
||||
sdk install java 17.0.3-ms && \
|
||||
sdk default java 17.0.3-ms"
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
image:
|
||||
file: .gitpod.Dockerfile
|
||||
|
||||
tasks:
|
||||
|
||||
- name: Run backend
|
||||
before: cd hertzbeat-manager
|
||||
command: |
|
||||
gp sync-await setup-backend
|
||||
mvn spring-boot:run
|
||||
|
||||
- name: Run frontend
|
||||
before: cd web-app
|
||||
command: |
|
||||
gp sync-await setup-frontend
|
||||
yarn start --public-host "`gp url 4200`"
|
||||
openMode: split-right
|
||||
|
||||
- name: Setup backend
|
||||
init: |
|
||||
mvn clean install -DskipTests
|
||||
command: |
|
||||
gp sync-done setup-backend
|
||||
exit 0
|
||||
|
||||
- name: Setup frontend
|
||||
init: |
|
||||
cd web-app && yarn install
|
||||
command: |
|
||||
gp sync-done setup-frontend
|
||||
exit 0
|
||||
openMode: split-right
|
||||
|
||||
vscode:
|
||||
extensions:
|
||||
- vscjava.vscode-java-pack
|
||||
- editorconfig.editorconfig
|
||||
- dbaeumer.vscode-eslint
|
||||
- stylelint.vscode-stylelint
|
||||
- DavidAnson.vscode-markdownlint
|
||||
- ms-azuretools.vscode-docker
|
||||
- cweijan.vscode-database-client2
|
||||
- GitHub.vscode-pull-request-github
|
||||
|
||||
ports:
|
||||
- port: 4200
|
||||
name: Hertzbeat
|
||||
onOpen: open-browser
|
||||
|
||||
- port: 1157
|
||||
onOpen: ignore
|
||||
|
||||
- port: 1158
|
||||
onOpen: ignore
|
||||
+6
-2
@@ -62,9 +62,13 @@ header:
|
||||
- '.stylelintrc'
|
||||
- '.prettierignore'
|
||||
- '.prettierrc.js'
|
||||
- '.gitpod.Dockerfile'
|
||||
- '.gitpod.yml'
|
||||
- 'karma.conf.js'
|
||||
- 'proxy.conf.js'
|
||||
- 'pnpm-lock.yaml'
|
||||
- '.helmignore'
|
||||
- 'script/helm/**'
|
||||
- 'web-app/src/app/core/**'
|
||||
- 'web-app/src/app/layout/**'
|
||||
- 'web-app/src/app/routes/exception/**'
|
||||
@@ -92,12 +96,12 @@ header:
|
||||
- 'web-app/src/style.less'
|
||||
- 'web-app/src/test.ts'
|
||||
- 'web-app/src/typings.d.ts'
|
||||
|
||||
|
||||
comment: on-failure
|
||||
|
||||
dependency:
|
||||
files:
|
||||
- pom.xml
|
||||
- web-app/package.json
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
{
|
||||
"globs": ["home/**/*.md"],
|
||||
"config": {
|
||||
"default": true,
|
||||
"MD001": true,
|
||||
"MD003": true,
|
||||
"MD013": {
|
||||
"line_length": 600,
|
||||
"code_blocks": false,
|
||||
"tables": false
|
||||
},
|
||||
"MD024": {
|
||||
"siblings_only": true
|
||||
},
|
||||
"MD025": true,
|
||||
"MD029": true,
|
||||
"MD033": {
|
||||
"allowed_elements": ["table", "tr", "td", "a", "img", "sub", "b", "br", "img", "tbody", "mark", "font"]
|
||||
},
|
||||
"MD036": false,
|
||||
"MD040": true,
|
||||
"MD045": true,
|
||||
"MD046": true,
|
||||
"MD047": true,
|
||||
"MD052": true
|
||||
},
|
||||
"ignores": [
|
||||
"**/node_modules/**",
|
||||
"**/target/**",
|
||||
"**/dist/**"
|
||||
]
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
# 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.
|
||||
wrapperVersion=3.3.2
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
|
||||
+49
-48
@@ -38,23 +38,23 @@ Even small corrections to typos are very welcome :)
|
||||
|
||||
3. Execute under the project root directory: `mvn clean install -DskipTests`
|
||||
|
||||
4. Start `springboot manager` service: `manager/src/main/java/org/apache/hertzbeat/manager/Manager.java`
|
||||
4. Add VM Options: `--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED`
|
||||
|
||||
5. Start `springboot manager` service: `manager/src/main/java/org/apache/hertzbeat/hertzbeat-manager/Manager.java`
|
||||
|
||||
#### Frontend start
|
||||
|
||||
1. Need `Node Yarn` Environment, Make sure `Node.js >= 18`
|
||||
1. Need `Node pnpm` Environment, Make sure `Node.js >= 18`
|
||||
|
||||
2. Cd to the `web-app` directory: `cd web-app`
|
||||
|
||||
3. Install yarn if not existed `npm install -g yarn`
|
||||
3. Install pnpm if not existed `npm install -g pnpm`
|
||||
|
||||
4. Install Dependencies: `yarn install` or `yarn install --registry=https://registry.npmmirror.com` in `web-app`
|
||||
4. Install Dependencies: `pnpm install` or `pnpm install --registry=https://registry.npmmirror.com` in `web-app`
|
||||
|
||||
5. Install angular-cli globally: `yarn global add @angular/cli@15` or `yarn global add @angular/cli@15 --registry=https://registry.npmmirror.com`
|
||||
5. After the local backend is started, start the local frontend in the web-app directory: `pnpm start`
|
||||
|
||||
6. After the local backend is started, start the local frontend in the web-app directory: `ng serve --open`
|
||||
|
||||
7. Browser access to localhost:4200 to start, default account/password is *admin/hertzbeat*
|
||||
6. Browser access to localhost:4200 to start, default account/password is *admin/hertzbeat*
|
||||
|
||||
|
||||
### Find tasks
|
||||
@@ -68,23 +68,23 @@ Of course, if you have a good idea, you can also propose it directly on GitHub D
|
||||
|
||||
1. First you need to fork your target [hertzbeat repository](https://github.com/apache/hertzbeat).
|
||||
2. Then download the code locally with git command:
|
||||
```shell
|
||||
git clone git@github.com:${YOUR_USERNAME}/hertzbeat.git #Recommended
|
||||
```
|
||||
```shell
|
||||
git clone git@github.com:${YOUR_USERNAME}/hertzbeat.git #Recommended
|
||||
```
|
||||
3. After the download is complete, please refer to the getting started guide or README file of the target repository to initialize the project.
|
||||
4. Then, you can refer to the following command to submit the code:
|
||||
```shell
|
||||
git checkout -b a-feature-branch #Recommended
|
||||
```
|
||||
```shell
|
||||
git checkout -b a-feature-branch #Recommended
|
||||
```
|
||||
5. Submit the coed as a commit, the commit message format specification required: [module name or type name]feature or bugfix or doc: custom message.
|
||||
```shell
|
||||
git add <modified file/path>
|
||||
git commit -m '[docs]feature: necessary instructions' #Recommended
|
||||
```
|
||||
```shell
|
||||
git add <modified file/path>
|
||||
git commit -m '[docs]feature: necessary instructions' #Recommended
|
||||
```
|
||||
6. Push to the remote repository
|
||||
```shell
|
||||
git push origin a-feature-branch
|
||||
```
|
||||
```shell
|
||||
git push origin a-feature-branch
|
||||
```
|
||||
7. Then you can initiate a new PR (Pull Request) on GitHub.
|
||||
|
||||
Please note that the title of the PR needs to conform to our spec, and write the necessary description in the PR to facilitate code review by Committers and other contributors.
|
||||
@@ -127,17 +127,17 @@ With the above steps, you are a contributor to HertzBeat. Repeat the previous st
|
||||
|
||||
[Join the Mailing Lists](https://lists.apache.org/list.html?dev@hertzbeat.apache.org) : Mail to `dev-subscribe@hertzbeat.apache.org` to subscribe mailing lists.
|
||||
|
||||
Add WeChat account `tan-cloud` to pull you into the WeChat group.
|
||||
Add WeChat account `ahertzbeat` to pull you into the WeChat group.
|
||||
|
||||
## 🥐 Architecture
|
||||
|
||||
- **[manager](https://github.com/apache/hertzbeat/tree/master/manager)** Provide monitoring management, system management basic services.
|
||||
- **[manager](https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager)** Provide monitoring management, system management basic services.
|
||||
> Provides monitoring management, monitoring configuration management, system user management, etc.
|
||||
- **[collector](https://github.com/apache/hertzbeat/tree/master/collector)** Provide metrics data collection services.
|
||||
> Use common protocols to remotely collect and obtain peer-to-peer metrics data.
|
||||
- **[warehouse](https://github.com/apache/hertzbeat/tree/master/warehouse)** Provide monitoring data warehousing services.
|
||||
> Metrics data management, data query, calculation and statistics.
|
||||
- **[alerter](https://github.com/apache/hertzbeat/tree/master/alerter)** Provide alert service.
|
||||
- **[alerter](https://github.com/apache/hertzbeat/tree/master/hertzbeat-alerter)** Provide alert service.
|
||||
> Alarm calculation trigger, monitoring status linkage, alarm configuration, and alarm notification.
|
||||
- **[web-app](https://github.com/apache/hertzbeat/tree/master/web-app)** Provide web ui.
|
||||
> Angular Web UI.
|
||||
@@ -189,23 +189,24 @@ Add WeChat account `tan-cloud` to pull you into the WeChat group.
|
||||
|
||||
3. 在项目根目录运行编译: `mvn clean install -DskipTests`
|
||||
|
||||
4. 启动`springboot manager`服务 `manager/src/main/java/org/apache/hertzbeat/manager/Manager.java`
|
||||
4. 在 `jvm` 加入参数 `--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED`
|
||||
|
||||
5. 启动`springboot manager`服务 `manager/src/main/java/org/apache/hertzbeat/hertzbeat-manager/Manager.java`
|
||||
|
||||
|
||||
#### 前端启动
|
||||
|
||||
1. 需要 `nodejs yarn` 环境, Make sure `Node.js >= 18`
|
||||
1. 需要 `nodejs pnpm` 环境, Make sure `Node.js >= 18`
|
||||
|
||||
2. 进入 `web-app` 目录: `cd web-app`
|
||||
|
||||
3. 安装yarn: `npm install -g yarn`
|
||||
3. 安装pnpm: `npm install -g pnpm`
|
||||
|
||||
4. 在前端工程目录 `web-app` 下执行: `yarn install` or `yarn install --registry=https://registry.npmmirror.com` in `web-app`
|
||||
4. 在前端工程目录 `web-app` 下执行: `pnpm install` or `pnpm install --registry=https://registry.npmmirror.com` in `web-app`
|
||||
|
||||
5. 全局安装 `angular-cli`: `yarn global add @angular/cli@15` or `yarn global add @angular/cli@15 --registry=https://registry.npmmirror.com`
|
||||
5. 待本地后端启动后,在web-app目录下启动本地前端 `pnpm start`
|
||||
|
||||
6. 待本地后端启动后,在web-app目录下启动本地前端 `ng serve --open`
|
||||
|
||||
7. 浏览器访问 localhost:4200 即可开始,默认账号密码 *admin/hertzbeat*
|
||||
6. 浏览器访问 localhost:4200 即可开始,默认账号密码 *admin/hertzbeat*
|
||||
|
||||
### 寻找任务
|
||||
|
||||
@@ -218,23 +219,23 @@ Add WeChat account `tan-cloud` to pull you into the WeChat group.
|
||||
|
||||
1. 首先您需要 Fork 目标仓库 [hertzbeat repository](https://github.com/apache/hertzbeat).
|
||||
2. 然后 用git命令 将代码下载到本地:
|
||||
```shell
|
||||
git clone git@github.com:${YOUR_USERNAME}/hertzbeat.git #Recommended
|
||||
```
|
||||
```shell
|
||||
git clone git@github.com:${YOUR_USERNAME}/hertzbeat.git #Recommended
|
||||
```
|
||||
3. 下载完成后,请参考目标仓库的入门指南或者 README 文件对项目进行初始化。
|
||||
4. 接着,您可以参考如下命令进行代码的提交, 切换新的分支, 进行开发:
|
||||
```shell
|
||||
git checkout -b a-feature-branch #Recommended
|
||||
```
|
||||
```shell
|
||||
git checkout -b a-feature-branch #Recommended
|
||||
```
|
||||
5. 提交 commit , commit 描述信息需要符合约定格式: [module name or type name]feature or bugfix or doc: custom message.
|
||||
```shell
|
||||
git add <modified file/path>
|
||||
git commit -m '[docs]feature: necessary instructions' #Recommended
|
||||
```
|
||||
```shell
|
||||
git add <modified file/path>
|
||||
git commit -m '[docs]feature: necessary instructions' #Recommended
|
||||
```
|
||||
6. 推送到远程仓库
|
||||
```shell
|
||||
git push origin a-feature-branch
|
||||
```
|
||||
```shell
|
||||
git push origin a-feature-branch
|
||||
```
|
||||
7. 然后您就可以在 GitHub 上发起新的 PR (Pull Request)。
|
||||
|
||||
请注意 PR 的标题需要符合我们的规范,并且在 PR 中写上必要的说明,来方便 Committer 和其他贡献者进行代码审查。
|
||||
@@ -277,17 +278,17 @@ git pull upstream master
|
||||
|
||||
[Join the Mailing Lists](https://lists.apache.org/list.html?dev@hertzbeat.apache.org) : Mail to `dev-subscribe@hertzbeat.apache.org` to subscribe mailing lists.
|
||||
|
||||
Add WeChat account `tan-cloud` to pull you into the WeChat group.
|
||||
Add WeChat account `ahertzbeat` to pull you into the WeChat group.
|
||||
|
||||
### 模块
|
||||
|
||||
- **[manager](https://github.com/apache/hertzbeat/tree/master/manager)** 提供监控管理,系统管理基础服务
|
||||
- **[manager](https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager)** 提供监控管理,系统管理基础服务
|
||||
> 提供对监控的管理,监控应用配置的管理,系统用户租户后台管理等。
|
||||
- **[collector](https://github.com/apache/hertzbeat/tree/master/collector)** 提供监控数据采集服务
|
||||
> 使用通用协议远程采集获取对端指标数据。
|
||||
- **[warehouse](https://github.com/apache/hertzbeat/tree/master/warehouse)** 提供监控数据仓储服务
|
||||
> 采集指标结果数据管理,数据落盘,查询,计算统计。
|
||||
- **[alerter](https://github.com/apache/hertzbeat/tree/master/alerter)** 提供告警服务
|
||||
- **[alerter](https://github.com/apache/hertzbeat/tree/master/hertzbeat-alerter)** 提供告警服务
|
||||
> 告警计算触发,任务状态联动,告警配置,告警通知。
|
||||
- **[web-app](https://github.com/apache/hertzbeat/tree/master/web-app)** 提供可视化控制台页面
|
||||
> 监控告警系统可视化控制台前端
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
Apache HertzBeat (incubating) is an effort undergoing incubation at the Apache
|
||||
Software Foundation (ASF), sponsored by the Apache Incubator PMC.
|
||||
|
||||
Incubation is required of all newly accepted projects until a further review
|
||||
indicates that the infrastructure, communications, and decision making process
|
||||
have stabilized in a manner consistent with other successful ASF projects.
|
||||
|
||||
While incubation status is not necessarily a reflection of the completeness
|
||||
or stability of the code, it does indicate that the project has yet to be
|
||||
fully endorsed by the ASF.
|
||||
@@ -1,5 +1,5 @@
|
||||
Apache HertzBeat (incubating)
|
||||
Copyright 2024 The Apache Software Foundation
|
||||
Apache HertzBeat™
|
||||
Copyright 2024-2025 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
|
||||
@@ -4,19 +4,23 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h4 align="center">
|
||||
<a href="README.md">English Document</a> | <a href="README_CN.md">中文文档</a>
|
||||
</h4>
|
||||
<p align="center">
|
||||
<b>Readme</b>:
|
||||
<b><a href="README.md">English</a></b> | <a href="README_CN.md">中文</a> | <a href="README_JP.md">日本語</a>
|
||||
</p>
|
||||
|
||||
> A real-time monitoring system with agentless, performance cluster, prometheus-compatible, custom monitoring and status page building capabilities.
|
||||
> A real-time observability system with agentless, performance cluster, prometheus-compatible, custom monitoring and status page building capabilities.
|
||||
|
||||
[](https://discord.gg/Fb6M73htGr)
|
||||
[](https://www.reddit.com/r/hertzbeat/)
|
||||
[](https://twitter.com/hertzbeat1024)
|
||||
[](https://x.com/hertzbeat1024)
|
||||
[](https://www.bestpractices.dev/projects/8139)
|
||||
[](https://hub.docker.com/repository/docker/apache/hertzbeat/general)
|
||||
[](https://app.codecov.io/gh/apache/hertzbeat)
|
||||
[](https://hub.docker.com/r/apache/hertzbeat)
|
||||
[](https://artifacthub.io/packages/search?repo=hertzbeat)
|
||||
[](https://www.youtube.com/channel/UCri75zfWX0GHqJFPENEbLow)
|
||||
[](https://gitpod.io/#https://github.com/apache/hertzbeat)
|
||||
[](https://deepwiki.com/apache/hertzbeat)
|
||||
|
||||
**Home: [hertzbeat.apache.org](https://hertzbeat.apache.org)**
|
||||
**Email: <a href="mailto:dev-subscribe@hertzbeat.apache.org">Mail to `dev-subscribe@hertzbeat.apache.org`</a> to subscribe mailing lists**
|
||||
@@ -24,7 +28,7 @@
|
||||
|
||||
## 🎡 <font color="green">Introduction</font>
|
||||
|
||||
[Apache HertzBeat](https://github.com/apache/hertzbeat) (incubating) is an easy-to-use, open source, real-time monitoring system with agentless, high performance cluster, prometheus-compatible, offers powerful custom monitoring and status page building capabilities.
|
||||
[Apache HertzBeat™](https://github.com/apache/hertzbeat) is an easy-to-use, open source, real-time observability system with agentless, high performance cluster, prometheus-compatible, offers powerful custom monitoring and status page building capabilities.
|
||||
|
||||
### Features
|
||||
|
||||
@@ -58,42 +62,42 @@
|
||||
> Welcome everyone to contribute your customized general monitoring type YML template during use.
|
||||
|
||||
|
||||
- [Website](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-website.yml), [Port Telnet](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-port.yml),
|
||||
[Http Api](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-api.yml), [Ping Connect](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ping.yml),
|
||||
[Jvm](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-jvm.yml), [SiteMap](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-fullsite.yml),
|
||||
[Ssl Certificate](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ssl_cert.yml), [SpringBoot2](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-springboot2.yml),
|
||||
[FTP Server](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ftp.yml), [SpringBoot3](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-springboot3.yml),
|
||||
[Udp Port](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-udp.yml), [Dns](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-dns.yml),
|
||||
[Pop3](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-pop3.yml), [Ntp](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ntp.yml),
|
||||
[Api Code](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-api_code.yml), [Smtp](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-smtp.yml),
|
||||
[Nginx](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-nginx.yml)
|
||||
- [Mysql](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-mysql.yml), [PostgreSQL](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-postgresql.yml),
|
||||
[MariaDB](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-mariadb.yml), [Redis](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redis.yml),
|
||||
[ElasticSearch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-elasticsearch.yml), [SqlServer](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-sqlserver.yml),
|
||||
[Oracle](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-oracle.yml), [MongoDB](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-mongodb.yml),
|
||||
[DM](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-dm.yml), [OpenGauss](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-opengauss.yml),
|
||||
[ClickHouse](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-clickhouse.yml), [IoTDB](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-iotdb.yml),
|
||||
[Redis Cluster](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redis_cluster.yml), [Redis Sentinel](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redis_sentinel.yml)
|
||||
[Doris BE](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-doris_be.yml), [Doris FE](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-doris_fe.yml),
|
||||
[Memcached](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-memcached.yml), [NebulaGraph](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-nebulaGraph.yml)
|
||||
- [Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-linux.yml), [Ubuntu](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ubuntu.yml),
|
||||
[CentOS](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-centos.yml), [Windows](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-windows.yml),
|
||||
[EulerOS](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-euleros.yml), [Fedora CoreOS](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-coreos.yml),
|
||||
[OpenSUSE](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-opensuse.yml), [Rocky Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-rockylinux.yml),
|
||||
[Red Hat](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redhat.yml), [FreeBSD](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-freebsd.yml),
|
||||
[AlmaLinux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-almalinux.yml), [Debian Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-debian.yml)
|
||||
- [Tomcat](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-tomcat.yml), [Nacos](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-nacos.yml),
|
||||
[Zookeeper](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-zookeeper.yml), [RabbitMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-rabbitmq.yml),
|
||||
[Flink](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-flink.yml), [Kafka](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-kafka.yml),
|
||||
[ShenYu](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-shenyu.yml), [DynamicTp](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-dynamic_tp.yml),
|
||||
[Jetty](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-jetty.yml), [ActiveMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-activemq.yml),
|
||||
[Spring Gateway](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-spring_gateway.yml), [EMQX MQTT](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-emqx.yml),
|
||||
[AirFlow](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-airflow.yml), [Hive](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-hive.yml),
|
||||
[Spark](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-spark.yml), [Hadoop](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-hadoop.yml)
|
||||
- [Kubernetes](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-kubernetes.yml), [Docker](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-docker.yml)
|
||||
- [CiscoSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-cisco_switch.yml), [HpeSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-hpe_switch.yml),
|
||||
[HuaweiSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-huawei_switch.yml), [TpLinkSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-tplink_switch.yml),
|
||||
[H3cSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-h3c_switch.yml)
|
||||
- [Website](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-website.yml), [Port Telnet](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-port.yml),
|
||||
[Http Api](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-api.yml), [Ping Connect](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ping.yml),
|
||||
[Jvm](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-jvm.yml), [SiteMap](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-fullsite.yml),
|
||||
[Ssl Certificate](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ssl_cert.yml), [SpringBoot2](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-springboot2.yml),
|
||||
[FTP Server](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ftp.yml), [SpringBoot3](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-springboot3.yml),
|
||||
[Udp Port](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-udp_port.yml), [Dns](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dns.yml),
|
||||
[Pop3](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-pop3.yml), [Ntp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ntp.yml),
|
||||
[Api Code](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-api_code.yml), [Smtp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-smtp.yml),
|
||||
[Nginx](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-nginx.yml)
|
||||
- [Mysql](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mysql.yml), [PostgreSQL](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-postgresql.yml),
|
||||
[MariaDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mariadb.yml), [Redis](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis.yml),
|
||||
[ElasticSearch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-elasticsearch.yml), [SqlServer](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-sqlserver.yml),
|
||||
[Oracle](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-oracle.yml), [MongoDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mongodb.yml),
|
||||
[DM](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dm.yml), [OpenGauss](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-opengauss.yml),
|
||||
[ClickHouse](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-clickhouse.yml), [IoTDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-iotdb.yml),
|
||||
[Redis Cluster](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis_cluster.yml), [Redis Sentinel](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis_sentinel.yml)
|
||||
[Doris BE](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-doris_be.yml), [Doris FE](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-doris_fe.yml),
|
||||
[Memcached](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-memcached.yml), [NebulaGraph](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-nebula_graph.yml)
|
||||
- [Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-linux.yml), [Ubuntu](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ubuntu.yml),
|
||||
[CentOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-centos.yml), [Windows](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-windows.yml),
|
||||
[EulerOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-euleros.yml), [Fedora CoreOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-coreos.yml),
|
||||
[OpenSUSE](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-opensuse.yml), [Rocky Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-rockylinux.yml),
|
||||
[Red Hat](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redhat.yml), [FreeBSD](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-freebsd.yml),
|
||||
[AlmaLinux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-almalinux.yml), [Debian Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-debian.yml)
|
||||
- [Tomcat](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-tomcat.yml), [Nacos](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-nacos.yml),
|
||||
[Zookeeper](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-zookeeper.yml), [RabbitMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-rabbitmq.yml),
|
||||
[Flink](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-flink.yml), [Kafka](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-kafka.yml),
|
||||
[ShenYu](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-shenyu.yml), [DynamicTp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dynamic_tp.yml),
|
||||
[Jetty](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-jetty.yml), [ActiveMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-activemq.yml),
|
||||
[Spring Gateway](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-spring_gateway.yml), [EMQX MQTT](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-emqx.yml),
|
||||
[AirFlow](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-airflow.yml), [Hive](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hive.yml),
|
||||
[Spark](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-spark.yml), [Hadoop](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hadoop.yml)
|
||||
- [Kubernetes](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-kubernetes.yml), [Docker](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-docker.yml)
|
||||
- [CiscoSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-cisco_switch.yml), [HpeSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hpe_switch.yml),
|
||||
[HuaweiSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-huawei_switch.yml), [TpLinkSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-tplink_switch.yml),
|
||||
[H3cSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-h3c_switch.yml)
|
||||
- And More Your Custom Template.
|
||||
- Notified Support `Discord` `Slack` `Telegram` `Email` `Dingtalk` `WeChat` `FeiShu` `Webhook` `SMS` `ServerChan`.
|
||||
|
||||
@@ -108,32 +112,36 @@
|
||||
|
||||
##### 1:Install quickly via docker
|
||||
|
||||
1. Just one command to get started:
|
||||
1. Just one command to get started
|
||||
|
||||
```docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat apache/hertzbeat```
|
||||
```shell
|
||||
docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat apache/hertzbeat
|
||||
```
|
||||
|
||||
2. Access `http://localhost:1157` to start, default account: `admin/hertzbeat`
|
||||
|
||||
3. Deploy collector clusters (Optional)
|
||||
|
||||
```
|
||||
docker run -d -e IDENTITY=custom-collector-name -e MANAGER_HOST=127.0.0.1 -e MANAGER_PORT=1158 --name hertzbeat-collector apache/hertzbeat-collector
|
||||
```
|
||||
- `-e IDENTITY=custom-collector-name` : set the collector unique identity name.
|
||||
- `-e MODE=public` : set the running mode(public or private), public cluster or private cloud-edge.
|
||||
- `-e MANAGER_HOST=127.0.0.1` : set the main hertzbeat server ip.
|
||||
- `-e MANAGER_PORT=1158` : set the main hertzbeat server port, default 1158.
|
||||
```shell
|
||||
docker run -d -e IDENTITY=custom-collector-name -e MANAGER_HOST=127.0.0.1 -e MANAGER_PORT=1158 --name hertzbeat-collector apache/hertzbeat-collector
|
||||
```
|
||||
|
||||
- `-e IDENTITY=custom-collector-name` : set the collector unique identity name.
|
||||
- `-e MODE=public` : set the running mode(public or private), public cluster or private cloud-edge.
|
||||
- `-e MANAGER_HOST=127.0.0.1` : set the main hertzbeat server ip.
|
||||
- `-e MANAGER_PORT=1158` : set the main hertzbeat server port, default 1158.
|
||||
|
||||
|
||||
Detailed config refer to [Install HertzBeat via Docker](https://hertzbeat.apache.org/docs/start/docker-deploy)
|
||||
|
||||
##### 2:Install via package
|
||||
|
||||
1. Download the release package `hertzbeat-xx.tar.gz` [GITHUB Release](https://github.com/apache/hertzbeat/releases)
|
||||
1. Download the release package `hertzbeat-xx.tar.gz` [Download](https://hertzbeat.apache.org/docs/download)
|
||||
2. Configure the HertzBeat configuration yml file `hertzbeat/config/application.yml` (optional)
|
||||
3. Run command `$ ./bin/startup.sh ` or `bin/startup.bat`
|
||||
4. Access `http://localhost:1157` to start, default account: `admin/hertzbeat`
|
||||
5. Deploy collector clusters (Optional)
|
||||
- Download the release package `hertzbeat-collector-xx.tar.gz` to new machine [GITHUB Release](https://github.com/apache/hertzbeat/releases)
|
||||
- Download the release package `hertzbeat-collector-xx.tar.gz` to new machine [Download](https://hertzbeat.apache.org/docs/download)
|
||||
- Configure the collector configuration yml file `hertzbeat-collector/config/application.yml`: unique `identity` name, running `mode` (public or private), hertzbeat `manager-host`, hertzbeat `manager-port`
|
||||
```yaml
|
||||
collector:
|
||||
@@ -154,19 +162,19 @@ Detailed config refer to [Install HertzBeat via Package](https://hertzbeat.apach
|
||||
##### 3:Start via source code
|
||||
|
||||
1. Local source code debugging needs to start the back-end project `manager` and the front-end project `web-app`.
|
||||
2. Backend:need `maven3+`, `java17`, `lombok`, start the `manager` service.
|
||||
2. Backend:need `maven3+`, `java17`, `lombok`, add VM options in IDE: ` --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED `, then start the `manager` service.
|
||||
3. Web:need `nodejs npm angular-cli` environment, Run `ng serve --open` in `web-app` directory after backend startup.
|
||||
4. Access `http://localhost:4200` to start, default account: `admin/hertzbeat`
|
||||
|
||||
Detailed steps refer to [CONTRIBUTING](CONTRIBUTING.md)
|
||||
|
||||
##### 4:Install All(hertzbeat+mysql+tsdb) via Docker-compose
|
||||
##### 4:Install All(hertzbeat+postgresql+tsdb) via Docker-compose
|
||||
|
||||
Install the mysql/postgresql database, iotdb/tdengine/victoria-metrics database and hertzbeat at one time through [docker-compose deployment script](script/docker-compose).
|
||||
Install the postgresql/mysql database, victoria-metrics/iotdb/tdengine database and hertzbeat at one time through [docker-compose deployment script](script/docker-compose).
|
||||
|
||||
Detailed steps refer to [Install via Docker-Compose](script/docker-compose/README.md)
|
||||
|
||||
##### 5. Install All(hertzbeat+collector+mysql+tsdb) via kubernetes helm charts
|
||||
##### 5: Install All(hertzbeat+collector+postgresql+tsdb) via kubernetes helm charts
|
||||
|
||||
Install HertzBeat cluster in a Kubernetes cluster by Helm chart.
|
||||
|
||||
@@ -174,10 +182,6 @@ Detailed steps refer to [Artifact Hub](https://artifacthub.io/packages/helm/hert
|
||||
|
||||
**HAVE FUN**
|
||||
|
||||
## 🥐 Roadmap
|
||||
|
||||

|
||||
|
||||
## ✨ Contributors
|
||||
|
||||
Thanks to these wonderful people, welcome to join us:
|
||||
@@ -220,7 +224,7 @@ Thanks to these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://mroldx.xyz/"><img src="https://avatars.githubusercontent.com/u/34847828?v=4?s=100" width="100px;" alt="mroldx"/><br /><sub><b>mroldx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mroldx" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/woshiniusange"><img src="https://avatars.githubusercontent.com/u/91513022?v=4?s=100" width="100px;" alt="woshiniusange"/><br /><sub><b>woshiniusange</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=woshiniusange" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://vampireachao.github.io/"><img src="https://avatars.githubusercontent.com/u/52746628?v=4?s=100" width="100px;" alt="VampireAchao"/><br /><sub><b>VampireAchao</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=VampireAchao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Ceilzcx"><img src="https://avatars.githubusercontent.com/u/48920254?v=4?s=100" width="100px;" alt="zcx"/><br /><sub><b>zcx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Ceilzcx" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ACeilzcx" title="Bug reports">🐛</a> <a href="#design-Ceilzcx" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Ceilzcx"><img src="https://avatars.githubusercontent.com/u/48920254?v=4?s=100" width="100px;" alt="zcx"/><br /><sub><b>zcx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Ceilzcx" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ACeilzcx" title="Bug reports">🐛</a> <a href="#design-Ceilzcx" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/commits?author=Ceilzcx" title="Tests">⚠️</a> <a href="#blog-Ceilzcx" title="Blogposts">📝</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/CharlieXCL"><img src="https://avatars.githubusercontent.com/u/91540487?v=4?s=100" width="100px;" alt="CharlieXCL"/><br /><sub><b>CharlieXCL</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=CharlieXCL" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Privauto"><img src="https://avatars.githubusercontent.com/u/36581456?v=4?s=100" width="100px;" alt="Privauto"/><br /><sub><b>Privauto</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Privauto" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Privauto" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
@@ -234,7 +238,7 @@ Thanks to these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.zhihu.com/people/kevinbauer"><img src="https://avatars.githubusercontent.com/u/28581579?v=4?s=100" width="100px;" alt="Kevin Huang"/><br /><sub><b>Kevin Huang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=kevinhuangwl" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TJxiaobao"><img src="https://avatars.githubusercontent.com/u/85919258?v=4?s=100" width="100px;" alt="铁甲小宝"/><br /><sub><b>铁甲小宝</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/issues?q=author%3ATJxiaobao" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=TJxiaobao" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=TJxiaobao" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TJxiaobao"><img src="https://avatars.githubusercontent.com/u/85919258?v=4?s=100" width="100px;" alt="铁甲小宝"/><br /><sub><b>铁甲小宝</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/issues?q=author%3ATJxiaobao" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=TJxiaobao" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=TJxiaobao" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=TJxiaobao" title="Tests">⚠️</a> <a href="#design-TJxiaobao" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Jack-123-power"><img src="https://avatars.githubusercontent.com/u/84333501?v=4?s=100" width="100px;" alt="Captain Jack"/><br /><sub><b>Captain Jack</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Jack-123-power" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/haibo-duan"><img src="https://avatars.githubusercontent.com/u/7974845?v=4?s=100" width="100px;" alt="haibo.duan"/><br /><sub><b>haibo.duan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=haibo-duan" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/commits?author=haibo-duan" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/assassinfym"><img src="https://avatars.githubusercontent.com/u/15188754?v=4?s=100" width="100px;" alt="assassin"/><br /><sub><b>assassin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/issues?q=author%3Aassassinfym" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=assassinfym" title="Code">💻</a></td>
|
||||
@@ -276,7 +280,7 @@ Thanks to these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://wjl110.xyz/"><img src="https://avatars.githubusercontent.com/u/53851034?v=4?s=100" width="100px;" alt="wjl110"/><br /><sub><b>wjl110</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wjl110" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ngyhd"><img src="https://avatars.githubusercontent.com/u/29095207?v=4?s=100" width="100px;" alt="Sean"/><br /><sub><b>Sean</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ngyhd" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Daydreamer-ia"><img src="https://avatars.githubusercontent.com/u/83362909?v=4?s=100" width="100px;" alt="chenyiqin"/><br /><sub><b>chenyiqin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Daydreamer-ia" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Daydreamer-ia" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hudongdong129"><img src="https://avatars.githubusercontent.com/u/34374227?v=4?s=100" width="100px;" alt="hudongdong129"/><br /><sub><b>hudongdong129</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hudongdong129"><img src="https://avatars.githubusercontent.com/u/34374227?v=4?s=100" width="100px;" alt="hudongdong129"/><br /><sub><b>hudongdong129</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Documentation">📖</a> <a href="#design-hudongdong129" title="Design">🎨</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TherChenYang"><img src="https://avatars.githubusercontent.com/u/124348939?v=4?s=100" width="100px;" alt="TherChenYang"/><br /><sub><b>TherChenYang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=TherChenYang" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=TherChenYang" title="Tests">⚠️</a></td>
|
||||
@@ -292,7 +296,7 @@ Thanks to these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/baiban114"><img src="https://avatars.githubusercontent.com/u/59152619?v=4?s=100" width="100px;" alt="TableRow"/><br /><sub><b>TableRow</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=baiban114" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=baiban114" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ByteIDance"><img src="https://avatars.githubusercontent.com/u/100207562?v=4?s=100" width="100px;" alt="ByteIDance"/><br /><sub><b>ByteIDance</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ByteIDance" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mangel2002"><img src="https://avatars.githubusercontent.com/u/9348020?v=4?s=100" width="100px;" alt="Jangfe"/><br /><sub><b>Jangfe</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mangel2002" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zqr10159"><img src="https://avatars.githubusercontent.com/u/30048352?v=4?s=100" width="100px;" alt="zqr10159"/><br /><sub><b>zqr10159</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zqr10159" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=zqr10159" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zqr10159"><img src="https://avatars.githubusercontent.com/u/30048352?v=4?s=100" width="100px;" alt="zqr10159"/><br /><sub><b>zqr10159</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zqr10159" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=zqr10159" title="Code">💻</a> <a href="#blog-zqr10159" title="Blogposts">📝</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Azqr10159" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=zqr10159" title="Tests">⚠️</a> <a href="#design-zqr10159" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/vinci-897"><img src="https://avatars.githubusercontent.com/u/55838224?v=4?s=100" width="100px;" alt="vinci"/><br /><sub><b>vinci</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=vinci-897" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=vinci-897" title="Documentation">📖</a> <a href="#design-vinci-897" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/js110"><img src="https://avatars.githubusercontent.com/u/51191863?v=4?s=100" width="100px;" alt="js110"/><br /><sub><b>js110</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=js110" title="Code">💻</a></td>
|
||||
</tr>
|
||||
@@ -306,8 +310,8 @@ Thanks to these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ZangJuxy"><img src="https://avatars.githubusercontent.com/u/71380295?v=4?s=100" width="100px;" alt="ZangJuxy"/><br /><sub><b>ZangJuxy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ZangJuxy" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/l646505418"><img src="https://avatars.githubusercontent.com/u/50475131?v=4?s=100" width="100px;" alt="l646505418"/><br /><sub><b>l646505418</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=l646505418" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.carpewang.com"><img src="https://avatars.githubusercontent.com/u/78642589?v=4?s=100" width="100px;" alt="Carpe-Wang"/><br /><sub><b>Carpe-Wang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Carpe-Wang" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/l646505418"><img src="https://avatars.githubusercontent.com/u/50475131?v=4?s=100" width="100px;" alt="l646505418"/><br /><sub><b>l646505418</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=l646505418" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Al646505418" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.carpewang.com"><img src="https://avatars.githubusercontent.com/u/78642589?v=4?s=100" width="100px;" alt="Carpe-Wang"/><br /><sub><b>Carpe-Wang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Carpe-Wang" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ACarpe-Wang" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/moshu023"><img src="https://avatars.githubusercontent.com/u/48593205?v=4?s=100" width="100px;" alt="莫枢"/><br /><sub><b>莫枢</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=moshu023" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/huangcanda"><img src="https://avatars.githubusercontent.com/u/4470566?v=4?s=100" width="100px;" alt="huangcanda"/><br /><sub><b>huangcanda</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=huangcanda" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.zrkizzy.com"><img src="https://avatars.githubusercontent.com/u/85340613?v=4?s=100" width="100px;" alt="世纪末的架构师"/><br /><sub><b>世纪末的架构师</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Architect-Java" title="Code">💻</a></td>
|
||||
@@ -357,7 +361,7 @@ Thanks to these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lisongning"><img src="https://avatars.githubusercontent.com/u/93140178?v=4?s=100" width="100px;" alt="lisongning"/><br /><sub><b>lisongning</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lisongning" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/YutingNie"><img src="https://avatars.githubusercontent.com/u/104416402?v=4?s=100" width="100px;" alt="YutingNie"/><br /><sub><b>YutingNie</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=YutingNie" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=YutingNie" title="Documentation">📖</a> <a href="#design-YutingNie" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mikezzb"><img src="https://avatars.githubusercontent.com/u/23418428?v=4?s=100" width="100px;" alt="Mike Zhou"/><br /><sub><b>Mike Zhou</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mikezzb" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=mikezzb" title="Documentation">📖</a> <a href="#design-mikezzb" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/a-little-fool"><img src="https://avatars.githubusercontent.com/u/105542329?v=4?s=100" width="100px;" alt="小笨蛋"/><br /><sub><b>小笨蛋</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=a-little-fool" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lynx009"><img src="https://avatars.githubusercontent.com/u/105542329?v=4?s=100" width="100px;" alt="lynx009"/><br /><sub><b>lynx009</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lynx009" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/littlezhongzer"><img src="https://avatars.githubusercontent.com/u/33685289?v=4?s=100" width="100px;" alt="littlezhongzer"/><br /><sub><b>littlezhongzer</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=littlezhongzer" title="Code">💻</a></td>
|
||||
@@ -366,7 +370,7 @@ Thanks to these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/XimfengYao"><img src="https://avatars.githubusercontent.com/u/17541537?v=4?s=100" width="100px;" alt="姚贤丰"/><br /><sub><b>姚贤丰</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=XimfengYao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LINGLUOJUN"><img src="https://avatars.githubusercontent.com/u/16778977?v=4?s=100" width="100px;" alt="lingluojun"/><br /><sub><b>lingluojun</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LINGLUOJUN" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.luelueking.com"><img src="https://avatars.githubusercontent.com/u/93204032?v=4?s=100" width="100px;" alt="1ue"/><br /><sub><b>1ue</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=luelueking" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.jimmyqiao.top"><img src="https://avatars.githubusercontent.com/u/67301054?v=4?s=100" width="100px;" alt="qyaaaa"/><br /><sub><b>qyaaaa</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=qyaaaa" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.jimmyqiao.top"><img src="https://avatars.githubusercontent.com/u/67301054?v=4?s=100" width="100px;" alt="qyaaaa"/><br /><sub><b>qyaaaa</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=qyaaaa" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Aqyaaaa" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://novohit.top"><img src="https://avatars.githubusercontent.com/u/101090395?v=4?s=100" width="100px;" alt="novohit"/><br /><sub><b>novohit</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=novohit" title="Code">💻</a></td>
|
||||
@@ -380,14 +384,14 @@ Thanks to these wonderful people, welcome to join us:
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/1036664317"><img src="https://avatars.githubusercontent.com/u/7696697?v=4?s=100" width="100px;" alt="moghn"/><br /><sub><b>moghn</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=1036664317" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xiaoguolong"><img src="https://avatars.githubusercontent.com/u/33684988?v=4?s=100" width="100px;" alt="xiaoguolong"/><br /><sub><b>xiaoguolong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xiaoguolong" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Clownsw"><img src="https://avatars.githubusercontent.com/u/28394742?v=4?s=100" width="100px;" alt="Smliexx"/><br /><sub><b>Smliexx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Clownsw" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Calvin979"><img src="https://avatars.githubusercontent.com/u/131688897?v=4?s=100" width="100px;" alt="Naruse"/><br /><sub><b>Naruse</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Calvin979" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=Calvin979" title="Code">💻</a> <a href="#design-Calvin979" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Clownsw"><img src="https://avatars.githubusercontent.com/u/28394742?v=4?s=100" width="100px;" alt="Smliexx"/><br /><sub><b>Smliexx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Clownsw" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AClownsw" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Calvin979"><img src="https://avatars.githubusercontent.com/u/131688897?v=4?s=100" width="100px;" alt="Calvin"/><br /><sub><b>Calvin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Calvin979" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=Calvin979" title="Code">💻</a> <a href="#design-Calvin979" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ACalvin979" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=Calvin979" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bbelide2"><img src="https://avatars.githubusercontent.com/u/26840796?v=4?s=100" width="100px;" alt="Bala Sukesh"/><br /><sub><b>Bala Sukesh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=bbelide2" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jinyaoMa"><img src="https://avatars.githubusercontent.com/u/25066570?v=4?s=100" width="100px;" alt="Jinyao Ma"/><br /><sub><b>Jinyao Ma</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jinyaoMa" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://linuxsuren.github.io/open-source-best-practice/"><img src="https://avatars.githubusercontent.com/u/1450685?v=4?s=100" width="100px;" alt="Rick"/><br /><sub><b>Rick</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LinuxSuRen" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=LinuxSuRen" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ZY945"><img src="https://avatars.githubusercontent.com/u/74083801?v=4?s=100" width="100px;" alt="东风"/><br /><sub><b>东风</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ZY945" title="Code">💻</a> <a href="#design-ZY945" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/commits?author=ZY945" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ZY945"><img src="https://avatars.githubusercontent.com/u/74083801?v=4?s=100" width="100px;" alt="东风"/><br /><sub><b>东风</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ZY945" title="Code">💻</a> <a href="#design-ZY945" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/commits?author=ZY945" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AZY945" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/prolevel1"><img src="https://avatars.githubusercontent.com/u/51995525?v=4?s=100" width="100px;" alt="sonam singh"/><br /><sub><b>sonam singh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=prolevel1" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ZhangZixuan1994"><img src="https://avatars.githubusercontent.com/u/20011653?v=4?s=100" width="100px;" alt="ZhangZixuan1994"/><br /><sub><b>ZhangZixuan1994</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ZhangZixuan1994" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hurenjie1"><img src="https://avatars.githubusercontent.com/u/40120355?v=4?s=100" width="100px;" alt="SHIG"/><br /><sub><b>SHIG</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=hurenjie1" title="Code">💻</a></td>
|
||||
@@ -397,29 +401,29 @@ Thanks to these wonderful people, welcome to join us:
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/avvCode"><img src="https://avatars.githubusercontent.com/u/113538532?v=4?s=100" width="100px;" alt="avv"/><br /><sub><b>avv</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=avvCode" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yqxxgh"><img src="https://avatars.githubusercontent.com/u/42080876?v=4?s=100" width="100px;" alt="yqxxgh"/><br /><sub><b>yqxxgh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yqxxgh" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yqxxgh"><img src="https://avatars.githubusercontent.com/u/42080876?v=4?s=100" width="100px;" alt="yqxxgh"/><br /><sub><b>yqxxgh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yqxxgh" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=yqxxgh" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Ayqxxgh" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/CharlieShi46"><img src="https://avatars.githubusercontent.com/u/149798885?v=4?s=100" width="100px;" alt="CharlieShi46"/><br /><sub><b>CharlieShi46</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=CharlieShi46" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Nctllnty"><img src="https://avatars.githubusercontent.com/u/33241818?v=4?s=100" width="100px;" alt="Nctllnty"/><br /><sub><b>Nctllnty</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Nctllnty" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Wang-Yonghao"><img src="https://avatars.githubusercontent.com/u/48146606?v=4?s=100" width="100px;" alt="Wang-Yonghao"/><br /><sub><b>Wang-Yonghao</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Wang-Yonghao" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.yuque.com/dudiao/yy"><img src="https://avatars.githubusercontent.com/u/38355949?v=4?s=100" width="100px;" alt="读钓"/><br /><sub><b>读钓</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=dudiao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/starmilkxin"><img src="https://avatars.githubusercontent.com/u/55646681?v=4?s=100" width="100px;" alt="Xin"/><br /><sub><b>Xin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=starmilkxin" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/starmilkxin"><img src="https://avatars.githubusercontent.com/u/55646681?v=4?s=100" width="100px;" alt="Xin"/><br /><sub><b>Xin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=starmilkxin" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Astarmilkxin" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/handy-git"><img src="https://avatars.githubusercontent.com/u/32837980?v=4?s=100" width="100px;" alt="handy"/><br /><sub><b>handy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=handy-git" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LiuTianyou"><img src="https://avatars.githubusercontent.com/u/30208283?v=4?s=100" width="100px;" alt="LiuTianyou"/><br /><sub><b>LiuTianyou</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LiuTianyou" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=LiuTianyou" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LiuTianyou"><img src="https://avatars.githubusercontent.com/u/30208283?v=4?s=100" width="100px;" alt="LiuTianyou"/><br /><sub><b>LiuTianyou</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LiuTianyou" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=LiuTianyou" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ALiuTianyou" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=LiuTianyou" title="Tests">⚠️</a> <a href="#blog-LiuTianyou" title="Blogposts">📝</a> <a href="#design-LiuTianyou" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/WinterKi1ler"><img src="https://avatars.githubusercontent.com/u/160592092?v=4?s=100" width="100px;" alt="WinterKi1ler"/><br /><sub><b>WinterKi1ler</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=WinterKi1ler" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://sharehoo.cn/"><img src="https://avatars.githubusercontent.com/u/45377370?v=4?s=100" width="100px;" alt="miki"/><br /><sub><b>miki</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=miki-hmt" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://codeflex.substack.com/"><img src="https://avatars.githubusercontent.com/u/85513042?v=4?s=100" width="100px;" alt="Keshav Carpenter"/><br /><sub><b>Keshav Carpenter</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=alpha951" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=alpha951" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/makechoicenow"><img src="https://avatars.githubusercontent.com/u/9911918?v=4?s=100" width="100px;" alt="makechoicenow"/><br /><sub><b>makechoicenow</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=makechoicenow" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gjjjj0101"><img src="https://avatars.githubusercontent.com/u/71874373?v=4?s=100" width="100px;" alt="Gao Jian"/><br /><sub><b>Gao Jian</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=gjjjj0101" title="Code">💻</a> <a href="#design-gjjjj0101" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gjjjj0101"><img src="https://avatars.githubusercontent.com/u/71874373?v=4?s=100" width="100px;" alt="Gao Jian"/><br /><sub><b>Gao Jian</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=gjjjj0101" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/commits?author=gjjjj0101" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=gjjjj0101" title="Documentation">📖</a> <a href="#design-gjjjj0101" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Agjjjj0101" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://jangto.tistory.com/"><img src="https://avatars.githubusercontent.com/u/37864182?v=4?s=100" width="100px;" alt="Hyeon Sung"/><br /><sub><b>Hyeon Sung</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=dukbong" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://crossoverjie.top/"><img src="https://avatars.githubusercontent.com/u/15684156?v=4?s=100" width="100px;" alt="crossoverJie"/><br /><sub><b>crossoverJie</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=crossoverJie" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=crossoverJie" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://jangto.tistory.com/"><img src="https://avatars.githubusercontent.com/u/37864182?v=4?s=100" width="100px;" alt="Hyeon Sung"/><br /><sub><b>Hyeon Sung</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=dukbong" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=dukbong" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://crossoverjie.top/"><img src="https://avatars.githubusercontent.com/u/15684156?v=4?s=100" width="100px;" alt="crossoverJie"/><br /><sub><b>crossoverJie</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=crossoverJie" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=crossoverJie" title="Documentation">📖</a> <a href="#blog-crossoverJie" title="Blogposts">📝</a> <a href="https://github.com/apache/hertzbeat/commits?author=crossoverJie" title="Tests">⚠️</a> <a href="#design-crossoverJie" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PeixyJ"><img src="https://avatars.githubusercontent.com/u/45998593?v=4?s=100" width="100px;" alt="PeixyJ"/><br /><sub><b>PeixyJ</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=PeixyJ" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Hi-Mr-Wind"><img src="https://avatars.githubusercontent.com/u/85803831?v=4?s=100" width="100px;" alt="风如歌"/><br /><sub><b>风如歌</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Hi-Mr-Wind" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MananPoojara"><img src="https://avatars.githubusercontent.com/u/104253184?v=4?s=100" width="100px;" alt="Manan Pujara"/><br /><sub><b>Manan Pujara</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MananPoojara" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xuziyang"><img src="https://avatars.githubusercontent.com/u/8465969?v=4?s=100" width="100px;" alt="xuziyang"/><br /><sub><b>xuziyang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xuziyang" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xuziyang"><img src="https://avatars.githubusercontent.com/u/8465969?v=4?s=100" width="100px;" alt="xuziyang"/><br /><sub><b>xuziyang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xuziyang" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=xuziyang" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Axuziyang" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lwqzz"><img src="https://avatars.githubusercontent.com/u/62584513?v=4?s=100" width="100px;" alt="lwqzz"/><br /><sub><b>lwqzz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lwqzz" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -427,25 +431,124 @@ Thanks to these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tomorrowshipyltm"><img src="https://avatars.githubusercontent.com/u/61336903?v=4?s=100" width="100px;" alt="tomorrowshipyltm"/><br /><sub><b>tomorrowshipyltm</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=tomorrowshipyltm" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/15613060203"><img src="https://avatars.githubusercontent.com/u/41351615?v=4?s=100" width="100px;" alt="栗磊"/><br /><sub><b>栗磊</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=15613060203" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Alanxtl"><img src="https://avatars.githubusercontent.com/u/25652981?v=4?s=100" width="100px;" alt="Alan"/><br /><sub><b>Alan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Alanxtl" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.hadoop.wiki/"><img src="https://avatars.githubusercontent.com/u/29418975?v=4?s=100" width="100px;" alt="Jast"/><br /><sub><b>Jast</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zhangshenghang" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zuobiao-zhou"><img src="https://avatars.githubusercontent.com/u/61108539?v=4?s=100" width="100px;" alt="Zhang Yuxuan"/><br /><sub><b>Zhang Yuxuan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zuobiao-zhou" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.hadoop.wiki/"><img src="https://avatars.githubusercontent.com/u/29418975?v=4?s=100" width="100px;" alt="Jast"/><br /><sub><b>Jast</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zhangshenghang" title="Code">💻</a> <a href="#ideas-zhangshenghang" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/apache/hertzbeat/commits?author=zhangshenghang" title="Documentation">📖</a> <a href="#blog-zhangshenghang" title="Blogposts">📝</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Azhangshenghang" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=zhangshenghang" title="Tests">⚠️</a> <a href="#design-zhangshenghang" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zuobiao-zhou"><img src="https://avatars.githubusercontent.com/u/61108539?v=4?s=100" width="100px;" alt="Zhang Yuxuan"/><br /><sub><b>Zhang Yuxuan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zuobiao-zhou" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=zuobiao-zhou" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Azuobiao-zhou" title="Bug reports">🐛</a> <a href="#blog-zuobiao-zhou" title="Blogposts">📝</a> <a href="https://github.com/apache/hertzbeat/commits?author=zuobiao-zhou" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Pzz-2021"><img src="https://avatars.githubusercontent.com/u/118056735?v=4?s=100" width="100px;" alt="P.P."/><br /><sub><b>P.P.</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Pzz-2021" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LLP2333"><img src="https://avatars.githubusercontent.com/u/61670545?v=4?s=100" width="100px;" alt="llp2333"/><br /><sub><b>llp2333</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LLP2333" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/HeartLinked"><img src="https://avatars.githubusercontent.com/u/78212101?v=4?s=100" width="100px;" alt="feiyang li"/><br /><sub><b>feiyang li</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=HeartLinked" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Aias00"><img src="https://avatars.githubusercontent.com/u/25810623?v=4?s=100" width="100px;" alt="aias00"/><br /><sub><b>aias00</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Aias00" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Aias00"><img src="https://avatars.githubusercontent.com/u/25810623?v=4?s=100" width="100px;" alt="aias00"/><br /><sub><b>aias00</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Aias00" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Aias00" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AAias00" title="Bug reports">🐛</a> <a href="#ideas-Aias00" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/apache/hertzbeat/commits?author=Aias00" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/westboy"><img src="https://avatars.githubusercontent.com/u/6385565?v=4?s=100" width="100px;" alt="Jin"/><br /><sub><b>Jin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=westboy" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.csdn.net/qq_52397471"><img src="https://avatars.githubusercontent.com/u/77964041?v=4?s=100" width="100px;" alt="YuLuo"/><br /><sub><b>YuLuo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yuluo-yx" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Yanshuming1"><img src="https://avatars.githubusercontent.com/u/118667222?v=4?s=100" width="100px;" alt="xiaoming student"/><br /><sub><b>xiaoming student</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Yanshuming1" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.csdn.net/qq_52397471"><img src="https://avatars.githubusercontent.com/u/77964041?v=4?s=100" width="100px;" alt="YuLuo"/><br /><sub><b>YuLuo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yuluo-yx" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Ayuluo-yx" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=yuluo-yx" title="Tests">⚠️</a> <a href="#blog-yuluo-yx" title="Blogposts">📝</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Yanshuming1"><img src="https://avatars.githubusercontent.com/u/118667222?v=4?s=100" width="100px;" alt="linDong"/><br /><sub><b>linDong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Yanshuming1" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Yanshuming1" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AYanshuming1" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lwjxy"><img src="https://avatars.githubusercontent.com/u/52726400?v=4?s=100" width="100px;" alt="lwjxy"/><br /><sub><b>lwjxy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lwjxy" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://thespica.github.io/"><img src="https://avatars.githubusercontent.com/u/119573640?v=4?s=100" width="100px;" alt="John"/><br /><sub><b>John</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Thespica" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://thespica.github.io/"><img src="https://avatars.githubusercontent.com/u/119573640?v=4?s=100" width="100px;" alt="John"/><br /><sub><b>John</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Thespica" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Thespica" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/boatrainlsz"><img src="https://avatars.githubusercontent.com/u/18243785?v=4?s=100" width="100px;" alt="boatrainlsz"/><br /><sub><b>boatrainlsz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=boatrainlsz" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.yitianyigexiangfa.com/"><img src="https://avatars.githubusercontent.com/u/3973419?v=4?s=100" width="100px;" alt="Bill Lau"/><br /><sub><b>Bill Lau</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=JavaProgrammerLB" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lw-yang"><img src="https://avatars.githubusercontent.com/u/23456873?v=4?s=100" width="100px;" alt="lwyang"/><br /><sub><b>lwyang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lw-yang" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xfl12345"><img src="https://avatars.githubusercontent.com/u/17960863?v=4?s=100" width="100px;" alt="xfl12345"/><br /><sub><b>xfl12345</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xfl12345" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yykaue"><img src="https://avatars.githubusercontent.com/u/22905143?v=4?s=100" width="100px;" alt="Limbo"/><br /><sub><b>Limbo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yykaue" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/irenhongyan"><img src="https://avatars.githubusercontent.com/u/53438321?v=4?s=100" width="100px;" alt="哈哈哈哈哈哈哈哈哈"/><br /><sub><b>哈哈哈哈哈哈哈哈哈</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=irenhongyan" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ileonli"><img src="https://avatars.githubusercontent.com/u/45332412?v=4?s=100" width="100px;" alt="Leon Li"/><br /><sub><b>Leon Li</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ileonli" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://fnil.net/"><img src="https://avatars.githubusercontent.com/u/14142?v=4?s=100" width="100px;" alt="dennis zhuang"/><br /><sub><b>dennis zhuang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=killme2008" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kerwin612"><img src="https://avatars.githubusercontent.com/u/3371163?v=4?s=100" width="100px;" alt="Kerwin Bryant"/><br /><sub><b>Kerwin Bryant</b></sub></a><br /><a href="#design-kerwin612" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/commits?author=kerwin612" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=kerwin612" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Akerwin612" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ShineDevelopment"><img src="https://avatars.githubusercontent.com/u/59306780?v=4?s=100" width="100px;" alt="daixianglong"/><br /><sub><b>daixianglong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ShineDevelopment" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mchgood"><img src="https://avatars.githubusercontent.com/u/38482005?v=4?s=100" width="100px;" alt="mchgood"/><br /><sub><b>mchgood</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mchgood" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pwallk"><img src="https://avatars.githubusercontent.com/u/69385076?v=4?s=100" width="100px;" alt="kangli"/><br /><sub><b>kangli</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pwallk" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=pwallk" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Apwallk" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cdphantom"><img src="https://avatars.githubusercontent.com/u/12674795?v=4?s=100" width="100px;" alt="cdphantom"/><br /><sub><b>cdphantom</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=cdphantom" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/asd108908382"><img src="https://avatars.githubusercontent.com/u/77717999?v=4?s=100" width="100px;" alt="jiawei.guo"/><br /><sub><b>jiawei.guo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=asd108908382" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/QBH-insist"><img src="https://avatars.githubusercontent.com/u/39401478?v=4?s=100" width="100px;" alt="QBH-insist"/><br /><sub><b>QBH-insist</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=QBH-insist" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jiangsh-ui"><img src="https://avatars.githubusercontent.com/u/86990361?v=4?s=100" width="100px;" alt="jiangsh"/><br /><sub><b>jiangsh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jiangsh-ui" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/keaifafafa"><img src="https://avatars.githubusercontent.com/u/83876361?v=4?s=100" width="100px;" alt="Keaifa"/><br /><sub><b>Keaifa</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=keaifafafa" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Akeaifafafa" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/loong95"><img src="https://avatars.githubusercontent.com/u/16333958?v=4?s=100" width="100px;" alt="Loong"/><br /><sub><b>Loong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=loong95" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ceekay47"><img src="https://avatars.githubusercontent.com/u/104664857?v=4?s=100" width="100px;" alt="Chandrakant Vankayalapati"/><br /><sub><b>Chandrakant Vankayalapati</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ceekay47" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MRgenial"><img src="https://avatars.githubusercontent.com/u/49973336?v=4?s=100" width="100px;" alt="b_mountain"/><br /><sub><b>b_mountain</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MRgenial" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TemirlanBasitov"><img src="https://avatars.githubusercontent.com/u/57500808?v=4?s=100" width="100px;" alt="TemirlanBasitov"/><br /><sub><b>TemirlanBasitov</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=TemirlanBasitov" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wyfvsfy"><img src="https://avatars.githubusercontent.com/u/11973517?v=4?s=100" width="100px;" alt="wyfvsfy"/><br /><sub><b>wyfvsfy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wyfvsfy" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sherry-peng2333"><img src="https://avatars.githubusercontent.com/u/70619577?v=4?s=100" width="100px;" alt="sherry-peng2333"/><br /><sub><b>sherry-peng2333</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=sherry-peng2333" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lixiaobaivv"><img src="https://avatars.githubusercontent.com/u/39290771?v=4?s=100" width="100px;" alt="Yzzz"/><br /><sub><b>Yzzz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lixiaobaivv" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.bckf.cn/"><img src="https://avatars.githubusercontent.com/u/13309008?v=4?s=100" width="100px;" alt="puruidong"/><br /><sub><b>puruidong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pruidong" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shinestare"><img src="https://avatars.githubusercontent.com/u/13570619?v=4?s=100" width="100px;" alt="shinestare"/><br /><sub><b>shinestare</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=shinestare" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/po-168"><img src="https://avatars.githubusercontent.com/u/185745593?v=4?s=100" width="100px;" alt="po-168"/><br /><sub><b>po-168</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=po-168" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/All-The-Best-for"><img src="https://avatars.githubusercontent.com/u/76414672?v=4?s=100" width="100px;" alt="wbs99"/><br /><sub><b>wbs99</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=All-The-Best-for" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/starryCoder"><img src="https://avatars.githubusercontent.com/u/46510059?v=4?s=100" width="100px;" alt="starryCoder"/><br /><sub><b>starryCoder</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=starryCoder" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hasimmollah"><img src="https://avatars.githubusercontent.com/u/32538599?v=4?s=100" width="100px;" alt="hasimmollah"/><br /><sub><b>hasimmollah</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=hasimmollah" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ayu-v0"><img src="https://avatars.githubusercontent.com/u/127600988?v=4?s=100" width="100px;" alt="Ayu"/><br /><sub><b>Ayu</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ayu-v0" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Rancho-7"><img src="https://avatars.githubusercontent.com/u/59016860?v=4?s=100" width="100px;" alt="Nick Guo"/><br /><sub><b>Nick Guo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Rancho-7" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=Rancho-7" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ARancho-7" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/doveLin0818"><img src="https://avatars.githubusercontent.com/u/190927907?v=4?s=100" width="100px;" alt="doveLin"/><br /><sub><b>doveLin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=doveLin0818" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://zzrl.cc/"><img src="https://avatars.githubusercontent.com/u/91836599?v=4?s=100" width="100px;" alt="yunfan24"/><br /><sub><b>yunfan24</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yunfan24" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=yunfan24" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Ayunfan24" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=yunfan24" title="Tests">⚠️</a> <a href="#blog-yunfan24" title="Blogposts">📝</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lctking"><img src="https://avatars.githubusercontent.com/u/168249998?v=4?s=100" width="100px;" alt="nullwli"/><br /><sub><b>nullwli</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lctking" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://simonsigre.com/"><img src="https://avatars.githubusercontent.com/u/14932913?v=4?s=100" width="100px;" alt="Simon Sigré"/><br /><sub><b>Simon Sigré</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=simonsigre" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=simonsigre" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.ponfee.cn/"><img src="https://avatars.githubusercontent.com/u/46117331?v=4?s=100" width="100px;" alt="ponfee"/><br /><sub><b>ponfee</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ponfee" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Vedant7789"><img src="https://avatars.githubusercontent.com/u/147625492?v=4?s=100" width="100px;" alt="Vedant7789"/><br /><sub><b>Vedant7789</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Vedant7789" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Craaaaazy77"><img src="https://avatars.githubusercontent.com/u/23025522?v=4?s=100" width="100px;" alt="Craaaaazy77"/><br /><sub><b>Craaaaazy77</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Craaaaazy77" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Suvrat1629"><img src="https://avatars.githubusercontent.com/u/140749446?v=4?s=100" width="100px;" alt="Suvrat1629"/><br /><sub><b>Suvrat1629</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Suvrat1629" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://ghyghoo8.github.io/"><img src="https://avatars.githubusercontent.com/u/363129?v=4?s=100" width="100px;" alt="ghy"/><br /><sub><b>ghy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ghyghoo8" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/helei1030"><img src="https://avatars.githubusercontent.com/u/11839080?v=4?s=100" width="100px;" alt="helei1030"/><br /><sub><b>helei1030</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=helei1030" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://medium.com/@pjfanning"><img src="https://avatars.githubusercontent.com/u/11783444?v=4?s=100" width="100px;" alt="PJ Fanning"/><br /><sub><b>PJ Fanning</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pjfanning" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Apjfanning" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=pjfanning" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MonsterChenzhuo"><img src="https://avatars.githubusercontent.com/u/60029759?v=4?s=100" width="100px;" alt="monster"/><br /><sub><b>monster</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MonsterChenzhuo" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MasamiYui"><img src="https://avatars.githubusercontent.com/u/22274133?v=4?s=100" width="100px;" alt="Sherlock Yin"/><br /><sub><b>Sherlock Yin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MasamiYui" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=MasamiYui" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AMasamiYui" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wanhao23"><img src="https://avatars.githubusercontent.com/u/29560961?v=4?s=100" width="100px;" alt="wanhao"/><br /><sub><b>wanhao</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wanhao23" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=wanhao23" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jonasHanhan"><img src="https://avatars.githubusercontent.com/u/130035609?v=4?s=100" width="100px;" alt="jonasHanhan"/><br /><sub><b>jonasHanhan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jonasHanhan" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/NikhilMurugesan"><img src="https://avatars.githubusercontent.com/u/49281792?v=4?s=100" width="100px;" alt="NikhilMurugesan"/><br /><sub><b>NikhilMurugesan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=NikhilMurugesan" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/myangle1120"><img src="https://avatars.githubusercontent.com/u/19237013?v=4?s=100" width="100px;" alt="myangle1120"/><br /><sub><b>myangle1120</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=myangle1120" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yasminvo"><img src="https://avatars.githubusercontent.com/u/107528848?v=4?s=100" width="100px;" alt="yasminvo"/><br /><sub><b>yasminvo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yasminvo" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/notbugggg"><img src="https://avatars.githubusercontent.com/u/147966331?v=4?s=100" width="100px;" alt="不关银渐层的事哦"/><br /><sub><b>不关银渐层的事哦</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=notbugggg" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Anotbugggg" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yyahang"><img src="https://avatars.githubusercontent.com/u/90464876?v=4?s=100" width="100px;" alt="yyahang"/><br /><sub><b>yyahang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yyahang" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JuJinPark"><img src="https://avatars.githubusercontent.com/u/44892459?v=4?s=100" width="100px;" alt="jujin"/><br /><sub><b>jujin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=JuJinPark" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=JuJinPark" title="Documentation">📖</a> <a href="#ideas-JuJinPark" title="Ideas, Planning, & Feedback">🤔</a> <a href="#blog-JuJinPark" title="Blogposts">📝</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LL-LIN"><img src="https://avatars.githubusercontent.com/u/43002118?v=4?s=100" width="100px;" alt="LL-LIN"/><br /><sub><b>LL-LIN</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LL-LIN" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ALL-LIN" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://bigcyy.github.io/"><img src="https://avatars.githubusercontent.com/u/73413979?v=4?s=100" width="100px;" alt="Yang Chen"/><br /><sub><b>Yang Chen</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=bigcyy" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=bigcyy" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Abigcyy" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sarthakeash"><img src="https://avatars.githubusercontent.com/u/74091160?v=4?s=100" width="100px;" alt="Sarthak Arora"/><br /><sub><b>Sarthak Arora</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=sarthakeash" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=sarthakeash" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PengJingzhao"><img src="https://avatars.githubusercontent.com/u/97368949?v=4?s=100" width="100px;" alt="彭镜肇"/><br /><sub><b>彭镜肇</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=PengJingzhao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gagaradio"><img src="https://avatars.githubusercontent.com/u/18532370?v=4?s=100" width="100px;" alt="Walter Jia"/><br /><sub><b>Walter Jia</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=gagaradio" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/boyucjz"><img src="https://avatars.githubusercontent.com/u/18730041?v=4?s=100" width="100px;" alt="boyucjz"/><br /><sub><b>boyucjz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=boyucjz" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Cyanty"><img src="https://avatars.githubusercontent.com/u/153884653?v=4?s=100" width="100px;" alt="Cyanty"/><br /><sub><b>Cyanty</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Cyanty" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Cyanty" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KevinLLF"><img src="https://avatars.githubusercontent.com/u/85452733?v=4?s=100" width="100px;" alt="Jay丿167"/><br /><sub><b>Jay丿167</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=KevinLLF" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Duansg"><img src="https://avatars.githubusercontent.com/u/112607719?v=4?s=100" width="100px;" alt="Duansg"/><br /><sub><b>Duansg</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Duansg" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xiaomizhou2"><img src="https://avatars.githubusercontent.com/u/47807926?v=4?s=100" width="100px;" alt="zhangyaxi"/><br /><sub><b>zhangyaxi</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xiaomizhou2" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=xiaomizhou2" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RainBondsongyg"><img src="https://avatars.githubusercontent.com/u/94501396?v=4?s=100" width="100px;" alt="songyg"/><br /><sub><b>songyg</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=RainBondsongyg" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lx1229"><img src="https://avatars.githubusercontent.com/u/44620005?v=4?s=100" width="100px;" alt="Liuxin"/><br /><sub><b>Liuxin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lx1229" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yy549159265"><img src="https://avatars.githubusercontent.com/u/40821310?v=4?s=100" width="100px;" alt="yy549159265"/><br /><sub><b>yy549159265</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yy549159265" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=yy549159265" title="Tests">⚠️</a> <a href="#design-yy549159265" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cto-huhang"><img src="https://avatars.githubusercontent.com/u/53338629?v=4?s=100" width="100px;" alt="cto-huhang"/><br /><sub><b>cto-huhang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=cto-huhang" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Saramanda9988"><img src="https://avatars.githubusercontent.com/u/176664901?v=4?s=100" width="100px;" alt="LunaRain_079"/><br /><sub><b>LunaRain_079</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Saramanda9988" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/delei"><img src="https://avatars.githubusercontent.com/u/17263766?v=4?s=100" width="100px;" alt="DeleiGuo"/><br /><sub><b>DeleiGuo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=delei" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=delei" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=delei" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Adelei" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/chingjustwe"><img src="https://avatars.githubusercontent.com/u/13643747?v=4?s=100" width="100px;" alt="Rocky, Chi"/><br /><sub><b>Rocky, Chi</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=chingjustwe" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rowankid"><img src="https://avatars.githubusercontent.com/u/18652781?v=4?s=100" width="100px;" alt="Wenqi Luo"/><br /><sub><b>Wenqi Luo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/issues?q=author%3Arowankid" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tuzuy"><img src="https://avatars.githubusercontent.com/u/95274591?v=4?s=100" width="100px;" alt="tuzuy"/><br /><sub><b>tuzuy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=tuzuy" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/carlpinto25"><img src="https://avatars.githubusercontent.com/u/117299909?v=4?s=100" width="100px;" alt="carl pinto"/><br /><sub><b>carl pinto</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=carlpinto25" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://cxhello.top/"><img src="https://avatars.githubusercontent.com/u/49056040?v=4?s=100" width="100px;" alt="cxhello"/><br /><sub><b>cxhello</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=cxhello" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jl15988"><img src="https://avatars.githubusercontent.com/u/70638770?v=4?s=100" width="100px;" alt="会功夫的李白"/><br /><sub><b>会功夫的李白</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jl15988" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.aytop.cloud/"><img src="https://avatars.githubusercontent.com/u/37127008?v=4?s=100" width="100px;" alt="Albert.Yang"/><br /><sub><b>Albert.Yang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=AlbertYang0801" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.tokenlen.top/"><img src="https://avatars.githubusercontent.com/u/150590575?v=4?s=100" width="100px;" alt="zhou yong kang"/><br /><sub><b>zhou yong kang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mengnankkkk" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/warrobe"><img src="https://avatars.githubusercontent.com/u/89446159?v=4?s=100" width="100px;" alt="warrobe"/><br /><sub><b>warrobe</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=warrobe" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Jetiaime"><img src="https://avatars.githubusercontent.com/u/93769000?v=4?s=100" width="100px;" alt="TeAmo"/><br /><sub><b>TeAmo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Jetiaime" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pentium100"><img src="https://avatars.githubusercontent.com/u/27917?v=4?s=100" width="100px;" alt="pentium100"/><br /><sub><b>pentium100</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pentium100" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dedyks"><img src="https://avatars.githubusercontent.com/u/23741665?v=4?s=100" width="100px;" alt="Dedy Kurniawan Santoso"/><br /><sub><b>Dedy Kurniawan Santoso</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=dedyks" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -463,17 +566,17 @@ Thanks to these wonderful people, welcome to join us:
|
||||
|
||||
[Chat On Discord](https://discord.gg/Fb6M73htGr)
|
||||
|
||||
WeChat Group : Add friend `tan-cloud` and invite to the group.
|
||||
WeChat Group : Add friend `ahertzbeat` and invite to the group.
|
||||
|
||||
WeChat Public : Search ID `usthecom`.
|
||||
|
||||
[QQ Group](https://jq.qq.com/?_wv=1027&k=Bud9OzdI) : Group num `630061200`
|
||||
[QQ Group](https://qm.qq.com/q/xxqecSC2cw) : Group num `1035688434`
|
||||
|
||||
[Github Discussion](https://github.com/apache/hertzbeat/discussions)
|
||||
|
||||
[Reddit Community](https://www.reddit.com/r/hertzbeat/)
|
||||
|
||||
[Follow Us Twitter](https://twitter.com/hertzbeat1024)
|
||||
[Follow Us Twitter](https://x.com/hertzbeat1024)
|
||||
|
||||
[Subscribe YouTube](https://www.youtube.com/channel/UCri75zfWX0GHqJFPENEbLow)
|
||||
|
||||
@@ -489,15 +592,9 @@ HertzBeat is built on so many great open source projects, thanks to them!
|
||||
<p align="left">
|
||||
<img src="./home/static/img/home/cncf-landscape-left-logo.svg" width="300"> <img src="./home/static/img/home/cncf-right-logo.svg" width="345" />
|
||||
<br /><br />
|
||||
HertzBeat has been included in the <a href="https://landscape.cncf.io/guide?item=observability-and-analysis--monitoring--hertzbeat#observability-and-analysis--monitoring">
|
||||
CNCF Observability And Analysis - Monitoring Landscape.</a>
|
||||
HertzBeat has been included in the <a href="https://landscape.cncf.io/?item=observability-and-analysis--observability--hertzbeat">
|
||||
CNCF Observability And Analysis - Observability Landscape.</a>
|
||||
</p>
|
||||
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#apache/hertzbeat&Date)
|
||||
|
||||
|
||||
## 🛡️ License
|
||||
[`Apache License, Version 2.0`](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
|
||||
+203
-107
@@ -4,19 +4,22 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h4 align="center">
|
||||
<a href="README_CN.md">中文文档</a> | <a href="README.md">English Document</a>
|
||||
</h4>
|
||||
<p align="center">
|
||||
<b>Readme</b>:
|
||||
<a href="README.md">English</a> | <b><a href="README_CN.md">中文</a></b> | <a href="README_JP.md">日本語</a>
|
||||
</p>
|
||||
|
||||
> 实时监控系统,无需 Agent,性能集群,兼容 Prometheus,自定义监控和状态页构建能力。
|
||||
> 实时观测系统,无需 Agent,性能集群,兼容 Prometheus,自定义监控和状态页构建能力。
|
||||
|
||||
[](https://discord.gg/Fb6M73htGr)
|
||||
[](https://www.reddit.com/r/hertzbeat/)
|
||||
[](https://twitter.com/hertzbeat1024)
|
||||
[](https://x.com/hertzbeat1024)
|
||||
[](https://www.bestpractices.dev/projects/8139)
|
||||
[](https://hub.docker.com/repository/docker/apache/hertzbeat/general)
|
||||
[](https://app.codecov.io/gh/apache/hertzbeat)
|
||||
[](https://hub.docker.com/r/apache/hertzbeat)
|
||||
[](https://artifacthub.io/packages/search?repo=hertzbeat)
|
||||
[](https://www.youtube.com/channel/UCri75zfWX0GHqJFPENEbLow)
|
||||
[](https://gitpod.io/#https://github.com/apache/hertzbeat)
|
||||
|
||||
|
||||
**官网: [hertzbeat.apache.org](https://hertzbeat.apache.org)**
|
||||
@@ -25,22 +28,22 @@
|
||||
|
||||
## 🎡 <font color="green">介绍</font>
|
||||
|
||||
[Apache HertzBeat](https://github.com/apache/hertzbeat) (incubating)是一个易用友好的开源实时监控告警系统,无需 Agent,高性能集群,兼容 Prometheus,提供强大的自定义监控和状态页构建能力。
|
||||
[Apache HertzBeat™](https://github.com/apache/hertzbeat) 是一个易用友好的开源实时监控告警系统,无需 Agent,高性能集群,兼容 Prometheus,提供强大的自定义监控和状态页构建能力。
|
||||
|
||||
### 特点
|
||||
|
||||
- 集 **监控+告警+通知** 为一体,支持对应用服务,应用程序,数据库,缓存,操作系统,大数据,中间件,Web 服务器,云原生,网络,自定义等监控阈值告警通知一步到位。
|
||||
- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,零上手学习成本。
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模版 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需配置下就能立刻适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 易用友好,无需 `Agent`,全 `WEB` 页面操作,鼠标点一点就能监控告警,无需学习成本。
|
||||
- 将 `Http, Jmx, Ssh, Snmp, Jdbc, Prometheus` 等协议规范可配置化,只需在浏览器配置监控模板 `YML` 就能使用这些协议去自定义采集想要的指标。您相信只需简单配置即可快速适配一款 `K8s` 或 `Docker` 等新的监控类型吗?
|
||||
- 兼容 `Prometheus` 的系统生态并且更多,只需页面操作就可以监控 `Prometheus` 所能监控的。
|
||||
- 高性能,支持多采集器集群横向扩展,支持多隔离网络监控,云边协同。
|
||||
- 自由的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。
|
||||
- 灵活的告警阈值规则,`邮件` `Discord` `Slack` `Telegram` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱` 等方式消息及时送达。
|
||||
- 提供强大的状态页构建能力,轻松向用户传达您产品服务的实时状态。
|
||||
|
||||
|
||||
> `HertzBeat`的强大自定义,多类型支持,高性能,易扩展,低耦合,希望能帮助用户快速搭建自有监控系统。
|
||||
|
||||
----
|
||||
----
|
||||
|
||||

|
||||
|
||||
@@ -48,7 +51,7 @@
|
||||
|
||||

|
||||
|
||||
----
|
||||
----
|
||||
|
||||
## 🥐 模块
|
||||
|
||||
@@ -56,46 +59,46 @@
|
||||
|
||||
## ⛄ 已支持
|
||||
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为 `yml` 监控模版,用户可以导入这些模版来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。
|
||||
> 我们将监控采集类型(mysql,jvm,k8s)都定义为 `yml` 监控模板,用户可以导入这些模板来支持对应类型的监控!
|
||||
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模板。
|
||||
|
||||
- [Website](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-website.yml), [Port Telnet](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-port.yml),
|
||||
[Http Api](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-api.yml), [Ping Connect](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ping.yml),
|
||||
[Jvm](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-jvm.yml), [SiteMap](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-fullsite.yml),
|
||||
[Ssl Certificate](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ssl_cert.yml), [SpringBoot2](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-springboot2.yml),
|
||||
[FTP Server](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ftp.yml), [SpringBoot3](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-springboot3.yml),
|
||||
[Udp Port](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-udp.yml), [Dns](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-dns.yml),
|
||||
[Pop3](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-pop3.yml), [Ntp](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ntp.yml),
|
||||
[Api Code](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-api_code.yml), [Smtp](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-smtp.yml),
|
||||
[Nginx](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-nginx.yml)
|
||||
- [Mysql](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-mysql.yml), [PostgreSQL](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-postgresql.yml),
|
||||
[MariaDB](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-mariadb.yml), [Redis](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redis.yml),
|
||||
[ElasticSearch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-elasticsearch.yml), [SqlServer](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-sqlserver.yml),
|
||||
[Oracle](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-oracle.yml), [MongoDB](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-mongodb.yml),
|
||||
[DM](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-dm.yml), [OpenGauss](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-opengauss.yml),
|
||||
[ClickHouse](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-clickhouse.yml), [IoTDB](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-iotdb.yml),
|
||||
[Redis Cluster](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redis_cluster.yml), [Redis Sentinel](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redis_sentinel.yml)
|
||||
[Doris BE](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-doris_be.yml), [Doris FE](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-doris_fe.yml),
|
||||
[Memcached](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-memcached.yml), [NebulaGraph](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-nebulaGraph.yml)
|
||||
- [Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-linux.yml), [Ubuntu](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ubuntu.yml),
|
||||
[CentOS](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-centos.yml), [Windows](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-windows.yml),
|
||||
[EulerOS](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-euleros.yml), [Fedora CoreOS](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-coreos.yml),
|
||||
[OpenSUSE](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-opensuse.yml), [Rocky Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-rockylinux.yml),
|
||||
[Red Hat](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redhat.yml), [FreeBSD](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-freebsd.yml),
|
||||
[AlmaLinux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-almalinux.yml), [Debian Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-debian.yml)
|
||||
- [Tomcat](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-tomcat.yml), [Nacos](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-nacos.yml),
|
||||
[Zookeeper](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-zookeeper.yml), [RabbitMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-rabbitmq.yml),
|
||||
[Flink](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-flink.yml), [Kafka](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-kafka.yml),
|
||||
[ShenYu](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-shenyu.yml), [DynamicTp](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-dynamic_tp.yml),
|
||||
[Jetty](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-jetty.yml), [ActiveMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-activemq.yml),
|
||||
[Spring Gateway](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-spring_gateway.yml), [EMQX MQTT](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-emqx.yml),
|
||||
[AirFlow](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-airflow.yml), [Hive](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-hive.yml),
|
||||
[Spark](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-spark.yml), [Hadoop](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-hadoop.yml)
|
||||
- [Kubernetes](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-kubernetes.yml), [Docker](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-docker.yml)
|
||||
- [CiscoSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-cisco_switch.yml), [HpeSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-hpe_switch.yml),
|
||||
[HuaweiSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-huawei_switch.yml), [TpLinkSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-tplink_switch.yml),
|
||||
[H3cSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-h3c_switch.yml)
|
||||
- 和更多自定义监控模版。
|
||||
- [Website](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-website.yml), [Port Telnet](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-port.yml),
|
||||
[Http Api](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-api.yml), [Ping Connect](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ping.yml),
|
||||
[Jvm](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-jvm.yml), [SiteMap](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-fullsite.yml),
|
||||
[Ssl Certificate](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ssl_cert.yml), [SpringBoot2](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-springboot2.yml),
|
||||
[FTP Server](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ftp.yml), [SpringBoot3](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-springboot3.yml),
|
||||
[Udp Port](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-udp_port.yml), [Dns](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dns.yml),
|
||||
[Pop3](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-pop3.yml), [Ntp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ntp.yml),
|
||||
[Api Code](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-api_code.yml), [Smtp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-smtp.yml),
|
||||
[Nginx](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-nginx.yml)
|
||||
- [Mysql](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mysql.yml), [PostgreSQL](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-postgresql.yml),
|
||||
[MariaDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mariadb.yml), [Redis](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis.yml),
|
||||
[ElasticSearch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-elasticsearch.yml), [SqlServer](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-sqlserver.yml),
|
||||
[Oracle](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-oracle.yml), [MongoDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mongodb.yml),
|
||||
[DM](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dm.yml), [OpenGauss](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-opengauss.yml),
|
||||
[ClickHouse](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-clickhouse.yml), [IoTDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-iotdb.yml),
|
||||
[Redis Cluster](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis_cluster.yml), [Redis Sentinel](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis_sentinel.yml)
|
||||
[Doris BE](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-doris_be.yml), [Doris FE](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-doris_fe.yml),
|
||||
[Memcached](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-memcached.yml), [NebulaGraph](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-nebula_graph.yml)
|
||||
- [Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-linux.yml), [Ubuntu](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ubuntu.yml),
|
||||
[CentOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-centos.yml), [Windows](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-windows.yml),
|
||||
[EulerOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-euleros.yml), [Fedora CoreOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-coreos.yml),
|
||||
[OpenSUSE](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-opensuse.yml), [Rocky Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-rockylinux.yml),
|
||||
[Red Hat](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redhat.yml), [FreeBSD](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-freebsd.yml),
|
||||
[AlmaLinux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-almalinux.yml), [Debian Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-debian.yml)
|
||||
- [Tomcat](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-tomcat.yml), [Nacos](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-nacos.yml),
|
||||
[Zookeeper](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-zookeeper.yml), [RabbitMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-rabbitmq.yml),
|
||||
[Flink](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-flink.yml), [Kafka](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-kafka.yml),
|
||||
[ShenYu](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-shenyu.yml), [DynamicTp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dynamic_tp.yml),
|
||||
[Jetty](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-jetty.yml), [ActiveMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-activemq.yml),
|
||||
[Spring Gateway](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-spring_gateway.yml), [EMQX MQTT](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-emqx.yml),
|
||||
[AirFlow](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-airflow.yml), [Hive](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hive.yml),
|
||||
[Spark](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-spark.yml), [Hadoop](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hadoop.yml)
|
||||
- [Kubernetes](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-kubernetes.yml), [Docker](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-docker.yml)
|
||||
- [CiscoSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-cisco_switch.yml), [HpeSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hpe_switch.yml),
|
||||
[HuaweiSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-huawei_switch.yml), [TpLinkSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-tplink_switch.yml),
|
||||
[H3cSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-h3c_switch.yml)
|
||||
- 和更多自定义监控模板。
|
||||
- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱`。
|
||||
|
||||
## 🐕 快速开始
|
||||
@@ -109,30 +112,34 @@
|
||||
|
||||
1. `docker` 环境仅需一条命令即可开始
|
||||
|
||||
```docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat apache/hertzbeat```
|
||||
```shell
|
||||
docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat apache/hertzbeat
|
||||
```
|
||||
|
||||
2. 浏览器访问 `http://localhost:1157` 即可开始,默认账号密码 `admin/hertzbeat`
|
||||
|
||||
3. 部署采集器集群(可选)
|
||||
3. 部署采集器集群(可选)
|
||||
|
||||
```shell
|
||||
docker run -d -e IDENTITY=custom-collector-name -e MANAGER_HOST=127.0.0.1 -e MANAGER_PORT=1158 --name hertzbeat-collector apache/hertzbeat-collector
|
||||
```
|
||||
|
||||
```
|
||||
docker run -d -e IDENTITY=custom-collector-name -e MANAGER_HOST=127.0.0.1 -e MANAGER_PORT=1158 --name hertzbeat-collector apache/hertzbeat-collector
|
||||
```
|
||||
- `-e IDENTITY=custom-collector-name` : 配置此采集器的唯一性标识符名称,多个采集器名称不能相同,建议自定义英文名称。
|
||||
- `-e MODE=public` : 配置运行模式(public or private), 公共集群模式或私有云边模式。
|
||||
- `-e MANAGER_HOST=127.0.0.1` : 配置连接主 HertaBeat 服务的对外 IP。
|
||||
- `-e MANAGER_PORT=1158` : 配置连接主 HertzBeat 服务的对外端口,默认1158。
|
||||
- `-e MODE=public` : 配置运行模式(public or private), 公共集群模式或私有云边模式。
|
||||
- `-e MANAGER_HOST=127.0.0.1` : 配置连接主 HertzBeat 服务的对外 IP。
|
||||
- `-e MANAGER_PORT=1158` : 配置连接主 HertzBeat 服务的对外端口,默认1158。
|
||||
|
||||
更多配置详细步骤参考 [通过Docker方式安装HertzBeat](https://hertzbeat.apache.org/docs/start/docker-deploy)
|
||||
|
||||
更多配置详细步骤参考 [通过 Docker 方式安装 HertzBeat](https://hertzbeat.apache.org/docs/start/docker-deploy)
|
||||
|
||||
##### 方式二:通过安装包安装
|
||||
|
||||
1. 下载您系统环境对应的安装包 `hertzbeat-xx.tar.gz` [GITHUB Release](https://github.com/apache/hertzbeat/releases)
|
||||
1. 下载您系统环境对应的安装包 `hertzbeat-xx.tar.gz` [Download](https://hertzbeat.apache.org/docs/download)
|
||||
2. 配置 HertzBeat 的配置文件 `hertzbeat/config/application.yml` (可选)
|
||||
3. 部署启动 `$ ./bin/startup.sh ` 或 `bin/startup.bat`
|
||||
4. 浏览器访问 `http://localhost:1157` 即可开始,默认账号密码 `admin/hertzbeat`
|
||||
5. 部署采集器集群(可选)
|
||||
- 下载采集器安装包 `hertzbeat-collector-xx.tar.gz` 到规划的另一台部署主机上 [GITHUB Release](https://github.com/apache/hertzbeat/releases)
|
||||
5. 部署采集器集群(可选)
|
||||
- 下载采集器安装包 `hertzbeat-collector-xx.tar.gz` 到规划的另一台部署主机上 [Download](https://hertzbeat.apache.org/docs/download)
|
||||
- 配置采集器的配置文件 `hertzbeat-collector/config/application.yml` 里面的连接主 HertzBeat 服务的对外 IP,端口,当前采集器名称(需保证唯一性)等参数 `identity` `mode` (public or private) `manager-host` `manager-port`
|
||||
```yaml
|
||||
collector:
|
||||
@@ -153,19 +160,19 @@ docker run -d -e IDENTITY=custom-collector-name -e MANAGER_HOST=127.0.0.1 -e MAN
|
||||
##### 方式三:本地代码启动
|
||||
|
||||
1. 此为前后端分离项目,本地代码调试需要分别启动后端工程 `manager` 和前端工程 `web-app`
|
||||
2. 后端:需要 `maven3+`, `java17` 和 `lombok` 环境,修改 `YML` 配置信息并启动 `manager` 服务
|
||||
2. 后端:需要 `maven3+`, `java17` 和 `lombok` 环境,修改 `YML` 配置信息,添加JVM参数`--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED`后启动 `manager` 服务即可。
|
||||
3. 前端:需要 `nodejs npm angular-cli`环境,待本地后端启动后,在 `web-app` 目录下启动 `ng serve --open`
|
||||
4. 浏览器访问 `http://localhost:4200` 即可开始,默认账号密码 `admin/hertzbeat`
|
||||
|
||||
详细步骤参考 [参与贡献之本地代码启动](CONTRIBUTING.md)
|
||||
|
||||
##### 方式四:Docker-Compose 统一安装 hertzbeat+mysql+tsdb
|
||||
##### 方式四:Docker-Compose 统一安装 hertzbeat+postgresql+tsdb
|
||||
|
||||
通过 [Docker-Compose 部署脚本](script/docker-compose) 一次性把 mysql/postgresql 数据库, iotdb/tdengine/victoria-metrics 时序数据库和 hertzbeat 安装部署。
|
||||
通过 [Docker-Compose 部署脚本](script/docker-compose) 一次性把 postgresql/mysql 数据库, victoria-metrics/iotdb/tdengine 时序数据库和 hertzbeat 安装部署。
|
||||
|
||||
详细步骤参考 [通过Docker-Compose安装HertzBeat](script/docker-compose/README.md)
|
||||
详细步骤参考 [通过 Docker-Compose 安装 HertzBeat](script/docker-compose/README.md)
|
||||
|
||||
##### 方式五:Kubernetes Helm Charts 部署 hertzbeat+collector+mysql+tsdb
|
||||
##### 方式五:Kubernetes Helm Charts 部署 hertzbeat+collector+postgresql+tsdb
|
||||
|
||||
通过 Helm Chart 一次性将 HertzBeat 集群组件部署到 Kubernetes 集群中。
|
||||
|
||||
@@ -173,10 +180,6 @@ docker run -d -e IDENTITY=custom-collector-name -e MANAGER_HOST=127.0.0.1 -e MAN
|
||||
|
||||
**HAVE FUN**
|
||||
|
||||
## 🥐 路线图
|
||||
|
||||

|
||||
|
||||
## ✨ Contributors
|
||||
|
||||
Thanks these wonderful people, welcome to join us:
|
||||
@@ -219,7 +222,7 @@ Thanks these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://mroldx.xyz/"><img src="https://avatars.githubusercontent.com/u/34847828?v=4?s=100" width="100px;" alt="mroldx"/><br /><sub><b>mroldx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mroldx" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/woshiniusange"><img src="https://avatars.githubusercontent.com/u/91513022?v=4?s=100" width="100px;" alt="woshiniusange"/><br /><sub><b>woshiniusange</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=woshiniusange" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://vampireachao.github.io/"><img src="https://avatars.githubusercontent.com/u/52746628?v=4?s=100" width="100px;" alt="VampireAchao"/><br /><sub><b>VampireAchao</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=VampireAchao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Ceilzcx"><img src="https://avatars.githubusercontent.com/u/48920254?v=4?s=100" width="100px;" alt="zcx"/><br /><sub><b>zcx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Ceilzcx" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ACeilzcx" title="Bug reports">🐛</a> <a href="#design-Ceilzcx" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Ceilzcx"><img src="https://avatars.githubusercontent.com/u/48920254?v=4?s=100" width="100px;" alt="zcx"/><br /><sub><b>zcx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Ceilzcx" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ACeilzcx" title="Bug reports">🐛</a> <a href="#design-Ceilzcx" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/commits?author=Ceilzcx" title="Tests">⚠️</a> <a href="#blog-Ceilzcx" title="Blogposts">📝</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/CharlieXCL"><img src="https://avatars.githubusercontent.com/u/91540487?v=4?s=100" width="100px;" alt="CharlieXCL"/><br /><sub><b>CharlieXCL</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=CharlieXCL" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Privauto"><img src="https://avatars.githubusercontent.com/u/36581456?v=4?s=100" width="100px;" alt="Privauto"/><br /><sub><b>Privauto</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Privauto" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Privauto" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
@@ -233,7 +236,7 @@ Thanks these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.zhihu.com/people/kevinbauer"><img src="https://avatars.githubusercontent.com/u/28581579?v=4?s=100" width="100px;" alt="Kevin Huang"/><br /><sub><b>Kevin Huang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=kevinhuangwl" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TJxiaobao"><img src="https://avatars.githubusercontent.com/u/85919258?v=4?s=100" width="100px;" alt="铁甲小宝"/><br /><sub><b>铁甲小宝</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/issues?q=author%3ATJxiaobao" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=TJxiaobao" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=TJxiaobao" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TJxiaobao"><img src="https://avatars.githubusercontent.com/u/85919258?v=4?s=100" width="100px;" alt="铁甲小宝"/><br /><sub><b>铁甲小宝</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/issues?q=author%3ATJxiaobao" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=TJxiaobao" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=TJxiaobao" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=TJxiaobao" title="Tests">⚠️</a> <a href="#design-TJxiaobao" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Jack-123-power"><img src="https://avatars.githubusercontent.com/u/84333501?v=4?s=100" width="100px;" alt="Captain Jack"/><br /><sub><b>Captain Jack</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Jack-123-power" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/haibo-duan"><img src="https://avatars.githubusercontent.com/u/7974845?v=4?s=100" width="100px;" alt="haibo.duan"/><br /><sub><b>haibo.duan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=haibo-duan" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/commits?author=haibo-duan" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/assassinfym"><img src="https://avatars.githubusercontent.com/u/15188754?v=4?s=100" width="100px;" alt="assassin"/><br /><sub><b>assassin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/issues?q=author%3Aassassinfym" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=assassinfym" title="Code">💻</a></td>
|
||||
@@ -275,7 +278,7 @@ Thanks these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://wjl110.xyz/"><img src="https://avatars.githubusercontent.com/u/53851034?v=4?s=100" width="100px;" alt="wjl110"/><br /><sub><b>wjl110</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wjl110" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ngyhd"><img src="https://avatars.githubusercontent.com/u/29095207?v=4?s=100" width="100px;" alt="Sean"/><br /><sub><b>Sean</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ngyhd" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Daydreamer-ia"><img src="https://avatars.githubusercontent.com/u/83362909?v=4?s=100" width="100px;" alt="chenyiqin"/><br /><sub><b>chenyiqin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Daydreamer-ia" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Daydreamer-ia" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hudongdong129"><img src="https://avatars.githubusercontent.com/u/34374227?v=4?s=100" width="100px;" alt="hudongdong129"/><br /><sub><b>hudongdong129</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hudongdong129"><img src="https://avatars.githubusercontent.com/u/34374227?v=4?s=100" width="100px;" alt="hudongdong129"/><br /><sub><b>hudongdong129</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Documentation">📖</a> <a href="#design-hudongdong129" title="Design">🎨</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TherChenYang"><img src="https://avatars.githubusercontent.com/u/124348939?v=4?s=100" width="100px;" alt="TherChenYang"/><br /><sub><b>TherChenYang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=TherChenYang" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=TherChenYang" title="Tests">⚠️</a></td>
|
||||
@@ -291,7 +294,7 @@ Thanks these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/baiban114"><img src="https://avatars.githubusercontent.com/u/59152619?v=4?s=100" width="100px;" alt="TableRow"/><br /><sub><b>TableRow</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=baiban114" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=baiban114" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ByteIDance"><img src="https://avatars.githubusercontent.com/u/100207562?v=4?s=100" width="100px;" alt="ByteIDance"/><br /><sub><b>ByteIDance</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ByteIDance" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mangel2002"><img src="https://avatars.githubusercontent.com/u/9348020?v=4?s=100" width="100px;" alt="Jangfe"/><br /><sub><b>Jangfe</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mangel2002" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zqr10159"><img src="https://avatars.githubusercontent.com/u/30048352?v=4?s=100" width="100px;" alt="zqr10159"/><br /><sub><b>zqr10159</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zqr10159" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=zqr10159" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zqr10159"><img src="https://avatars.githubusercontent.com/u/30048352?v=4?s=100" width="100px;" alt="zqr10159"/><br /><sub><b>zqr10159</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zqr10159" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=zqr10159" title="Code">💻</a> <a href="#blog-zqr10159" title="Blogposts">📝</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Azqr10159" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=zqr10159" title="Tests">⚠️</a> <a href="#design-zqr10159" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/vinci-897"><img src="https://avatars.githubusercontent.com/u/55838224?v=4?s=100" width="100px;" alt="vinci"/><br /><sub><b>vinci</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=vinci-897" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=vinci-897" title="Documentation">📖</a> <a href="#design-vinci-897" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/js110"><img src="https://avatars.githubusercontent.com/u/51191863?v=4?s=100" width="100px;" alt="js110"/><br /><sub><b>js110</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=js110" title="Code">💻</a></td>
|
||||
</tr>
|
||||
@@ -305,8 +308,8 @@ Thanks these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ZangJuxy"><img src="https://avatars.githubusercontent.com/u/71380295?v=4?s=100" width="100px;" alt="ZangJuxy"/><br /><sub><b>ZangJuxy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ZangJuxy" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/l646505418"><img src="https://avatars.githubusercontent.com/u/50475131?v=4?s=100" width="100px;" alt="l646505418"/><br /><sub><b>l646505418</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=l646505418" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.carpewang.com"><img src="https://avatars.githubusercontent.com/u/78642589?v=4?s=100" width="100px;" alt="Carpe-Wang"/><br /><sub><b>Carpe-Wang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Carpe-Wang" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/l646505418"><img src="https://avatars.githubusercontent.com/u/50475131?v=4?s=100" width="100px;" alt="l646505418"/><br /><sub><b>l646505418</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=l646505418" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Al646505418" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.carpewang.com"><img src="https://avatars.githubusercontent.com/u/78642589?v=4?s=100" width="100px;" alt="Carpe-Wang"/><br /><sub><b>Carpe-Wang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Carpe-Wang" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ACarpe-Wang" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/moshu023"><img src="https://avatars.githubusercontent.com/u/48593205?v=4?s=100" width="100px;" alt="莫枢"/><br /><sub><b>莫枢</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=moshu023" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/huangcanda"><img src="https://avatars.githubusercontent.com/u/4470566?v=4?s=100" width="100px;" alt="huangcanda"/><br /><sub><b>huangcanda</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=huangcanda" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.zrkizzy.com"><img src="https://avatars.githubusercontent.com/u/85340613?v=4?s=100" width="100px;" alt="世纪末的架构师"/><br /><sub><b>世纪末的架构师</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Architect-Java" title="Code">💻</a></td>
|
||||
@@ -356,7 +359,7 @@ Thanks these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lisongning"><img src="https://avatars.githubusercontent.com/u/93140178?v=4?s=100" width="100px;" alt="lisongning"/><br /><sub><b>lisongning</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lisongning" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/YutingNie"><img src="https://avatars.githubusercontent.com/u/104416402?v=4?s=100" width="100px;" alt="YutingNie"/><br /><sub><b>YutingNie</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=YutingNie" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=YutingNie" title="Documentation">📖</a> <a href="#design-YutingNie" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mikezzb"><img src="https://avatars.githubusercontent.com/u/23418428?v=4?s=100" width="100px;" alt="Mike Zhou"/><br /><sub><b>Mike Zhou</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mikezzb" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=mikezzb" title="Documentation">📖</a> <a href="#design-mikezzb" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/a-little-fool"><img src="https://avatars.githubusercontent.com/u/105542329?v=4?s=100" width="100px;" alt="小笨蛋"/><br /><sub><b>小笨蛋</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=a-little-fool" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lynx009"><img src="https://avatars.githubusercontent.com/u/105542329?v=4?s=100" width="100px;" alt="lynx009"/><br /><sub><b>lynx009</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lynx009" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/littlezhongzer"><img src="https://avatars.githubusercontent.com/u/33685289?v=4?s=100" width="100px;" alt="littlezhongzer"/><br /><sub><b>littlezhongzer</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=littlezhongzer" title="Code">💻</a></td>
|
||||
@@ -365,7 +368,7 @@ Thanks these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/XimfengYao"><img src="https://avatars.githubusercontent.com/u/17541537?v=4?s=100" width="100px;" alt="姚贤丰"/><br /><sub><b>姚贤丰</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=XimfengYao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LINGLUOJUN"><img src="https://avatars.githubusercontent.com/u/16778977?v=4?s=100" width="100px;" alt="lingluojun"/><br /><sub><b>lingluojun</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LINGLUOJUN" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.luelueking.com"><img src="https://avatars.githubusercontent.com/u/93204032?v=4?s=100" width="100px;" alt="1ue"/><br /><sub><b>1ue</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=luelueking" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.jimmyqiao.top"><img src="https://avatars.githubusercontent.com/u/67301054?v=4?s=100" width="100px;" alt="qyaaaa"/><br /><sub><b>qyaaaa</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=qyaaaa" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.jimmyqiao.top"><img src="https://avatars.githubusercontent.com/u/67301054?v=4?s=100" width="100px;" alt="qyaaaa"/><br /><sub><b>qyaaaa</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=qyaaaa" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Aqyaaaa" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://novohit.top"><img src="https://avatars.githubusercontent.com/u/101090395?v=4?s=100" width="100px;" alt="novohit"/><br /><sub><b>novohit</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=novohit" title="Code">💻</a></td>
|
||||
@@ -379,14 +382,14 @@ Thanks these wonderful people, welcome to join us:
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/1036664317"><img src="https://avatars.githubusercontent.com/u/7696697?v=4?s=100" width="100px;" alt="moghn"/><br /><sub><b>moghn</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=1036664317" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xiaoguolong"><img src="https://avatars.githubusercontent.com/u/33684988?v=4?s=100" width="100px;" alt="xiaoguolong"/><br /><sub><b>xiaoguolong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xiaoguolong" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Clownsw"><img src="https://avatars.githubusercontent.com/u/28394742?v=4?s=100" width="100px;" alt="Smliexx"/><br /><sub><b>Smliexx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Clownsw" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Calvin979"><img src="https://avatars.githubusercontent.com/u/131688897?v=4?s=100" width="100px;" alt="Naruse"/><br /><sub><b>Naruse</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Calvin979" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=Calvin979" title="Code">💻</a> <a href="#design-Calvin979" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Clownsw"><img src="https://avatars.githubusercontent.com/u/28394742?v=4?s=100" width="100px;" alt="Smliexx"/><br /><sub><b>Smliexx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Clownsw" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AClownsw" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Calvin979"><img src="https://avatars.githubusercontent.com/u/131688897?v=4?s=100" width="100px;" alt="Calvin"/><br /><sub><b>Calvin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Calvin979" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=Calvin979" title="Code">💻</a> <a href="#design-Calvin979" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ACalvin979" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=Calvin979" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bbelide2"><img src="https://avatars.githubusercontent.com/u/26840796?v=4?s=100" width="100px;" alt="Bala Sukesh"/><br /><sub><b>Bala Sukesh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=bbelide2" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jinyaoMa"><img src="https://avatars.githubusercontent.com/u/25066570?v=4?s=100" width="100px;" alt="Jinyao Ma"/><br /><sub><b>Jinyao Ma</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jinyaoMa" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://linuxsuren.github.io/open-source-best-practice/"><img src="https://avatars.githubusercontent.com/u/1450685?v=4?s=100" width="100px;" alt="Rick"/><br /><sub><b>Rick</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LinuxSuRen" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=LinuxSuRen" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ZY945"><img src="https://avatars.githubusercontent.com/u/74083801?v=4?s=100" width="100px;" alt="东风"/><br /><sub><b>东风</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ZY945" title="Code">💻</a> <a href="#design-ZY945" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/commits?author=ZY945" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ZY945"><img src="https://avatars.githubusercontent.com/u/74083801?v=4?s=100" width="100px;" alt="东风"/><br /><sub><b>东风</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ZY945" title="Code">💻</a> <a href="#design-ZY945" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/commits?author=ZY945" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AZY945" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/prolevel1"><img src="https://avatars.githubusercontent.com/u/51995525?v=4?s=100" width="100px;" alt="sonam singh"/><br /><sub><b>sonam singh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=prolevel1" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ZhangZixuan1994"><img src="https://avatars.githubusercontent.com/u/20011653?v=4?s=100" width="100px;" alt="ZhangZixuan1994"/><br /><sub><b>ZhangZixuan1994</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ZhangZixuan1994" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hurenjie1"><img src="https://avatars.githubusercontent.com/u/40120355?v=4?s=100" width="100px;" alt="SHIG"/><br /><sub><b>SHIG</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=hurenjie1" title="Code">💻</a></td>
|
||||
@@ -396,29 +399,29 @@ Thanks these wonderful people, welcome to join us:
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/avvCode"><img src="https://avatars.githubusercontent.com/u/113538532?v=4?s=100" width="100px;" alt="avv"/><br /><sub><b>avv</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=avvCode" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yqxxgh"><img src="https://avatars.githubusercontent.com/u/42080876?v=4?s=100" width="100px;" alt="yqxxgh"/><br /><sub><b>yqxxgh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yqxxgh" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yqxxgh"><img src="https://avatars.githubusercontent.com/u/42080876?v=4?s=100" width="100px;" alt="yqxxgh"/><br /><sub><b>yqxxgh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yqxxgh" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=yqxxgh" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Ayqxxgh" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/CharlieShi46"><img src="https://avatars.githubusercontent.com/u/149798885?v=4?s=100" width="100px;" alt="CharlieShi46"/><br /><sub><b>CharlieShi46</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=CharlieShi46" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Nctllnty"><img src="https://avatars.githubusercontent.com/u/33241818?v=4?s=100" width="100px;" alt="Nctllnty"/><br /><sub><b>Nctllnty</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Nctllnty" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Wang-Yonghao"><img src="https://avatars.githubusercontent.com/u/48146606?v=4?s=100" width="100px;" alt="Wang-Yonghao"/><br /><sub><b>Wang-Yonghao</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Wang-Yonghao" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.yuque.com/dudiao/yy"><img src="https://avatars.githubusercontent.com/u/38355949?v=4?s=100" width="100px;" alt="读钓"/><br /><sub><b>读钓</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=dudiao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/starmilkxin"><img src="https://avatars.githubusercontent.com/u/55646681?v=4?s=100" width="100px;" alt="Xin"/><br /><sub><b>Xin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=starmilkxin" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/starmilkxin"><img src="https://avatars.githubusercontent.com/u/55646681?v=4?s=100" width="100px;" alt="Xin"/><br /><sub><b>Xin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=starmilkxin" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Astarmilkxin" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/handy-git"><img src="https://avatars.githubusercontent.com/u/32837980?v=4?s=100" width="100px;" alt="handy"/><br /><sub><b>handy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=handy-git" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LiuTianyou"><img src="https://avatars.githubusercontent.com/u/30208283?v=4?s=100" width="100px;" alt="LiuTianyou"/><br /><sub><b>LiuTianyou</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LiuTianyou" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=LiuTianyou" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LiuTianyou"><img src="https://avatars.githubusercontent.com/u/30208283?v=4?s=100" width="100px;" alt="LiuTianyou"/><br /><sub><b>LiuTianyou</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LiuTianyou" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=LiuTianyou" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ALiuTianyou" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=LiuTianyou" title="Tests">⚠️</a> <a href="#blog-LiuTianyou" title="Blogposts">📝</a> <a href="#design-LiuTianyou" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/WinterKi1ler"><img src="https://avatars.githubusercontent.com/u/160592092?v=4?s=100" width="100px;" alt="WinterKi1ler"/><br /><sub><b>WinterKi1ler</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=WinterKi1ler" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://sharehoo.cn/"><img src="https://avatars.githubusercontent.com/u/45377370?v=4?s=100" width="100px;" alt="miki"/><br /><sub><b>miki</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=miki-hmt" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://codeflex.substack.com/"><img src="https://avatars.githubusercontent.com/u/85513042?v=4?s=100" width="100px;" alt="Keshav Carpenter"/><br /><sub><b>Keshav Carpenter</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=alpha951" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=alpha951" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/makechoicenow"><img src="https://avatars.githubusercontent.com/u/9911918?v=4?s=100" width="100px;" alt="makechoicenow"/><br /><sub><b>makechoicenow</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=makechoicenow" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gjjjj0101"><img src="https://avatars.githubusercontent.com/u/71874373?v=4?s=100" width="100px;" alt="Gao Jian"/><br /><sub><b>Gao Jian</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=gjjjj0101" title="Code">💻</a> <a href="#design-gjjjj0101" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gjjjj0101"><img src="https://avatars.githubusercontent.com/u/71874373?v=4?s=100" width="100px;" alt="Gao Jian"/><br /><sub><b>Gao Jian</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=gjjjj0101" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/commits?author=gjjjj0101" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=gjjjj0101" title="Documentation">📖</a> <a href="#design-gjjjj0101" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Agjjjj0101" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://jangto.tistory.com/"><img src="https://avatars.githubusercontent.com/u/37864182?v=4?s=100" width="100px;" alt="Hyeon Sung"/><br /><sub><b>Hyeon Sung</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=dukbong" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://crossoverjie.top/"><img src="https://avatars.githubusercontent.com/u/15684156?v=4?s=100" width="100px;" alt="crossoverJie"/><br /><sub><b>crossoverJie</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=crossoverJie" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=crossoverJie" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://jangto.tistory.com/"><img src="https://avatars.githubusercontent.com/u/37864182?v=4?s=100" width="100px;" alt="Hyeon Sung"/><br /><sub><b>Hyeon Sung</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=dukbong" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=dukbong" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://crossoverjie.top/"><img src="https://avatars.githubusercontent.com/u/15684156?v=4?s=100" width="100px;" alt="crossoverJie"/><br /><sub><b>crossoverJie</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=crossoverJie" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=crossoverJie" title="Documentation">📖</a> <a href="#blog-crossoverJie" title="Blogposts">📝</a> <a href="https://github.com/apache/hertzbeat/commits?author=crossoverJie" title="Tests">⚠️</a> <a href="#design-crossoverJie" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PeixyJ"><img src="https://avatars.githubusercontent.com/u/45998593?v=4?s=100" width="100px;" alt="PeixyJ"/><br /><sub><b>PeixyJ</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=PeixyJ" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Hi-Mr-Wind"><img src="https://avatars.githubusercontent.com/u/85803831?v=4?s=100" width="100px;" alt="风如歌"/><br /><sub><b>风如歌</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Hi-Mr-Wind" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MananPoojara"><img src="https://avatars.githubusercontent.com/u/104253184?v=4?s=100" width="100px;" alt="Manan Pujara"/><br /><sub><b>Manan Pujara</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MananPoojara" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xuziyang"><img src="https://avatars.githubusercontent.com/u/8465969?v=4?s=100" width="100px;" alt="xuziyang"/><br /><sub><b>xuziyang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xuziyang" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xuziyang"><img src="https://avatars.githubusercontent.com/u/8465969?v=4?s=100" width="100px;" alt="xuziyang"/><br /><sub><b>xuziyang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xuziyang" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=xuziyang" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Axuziyang" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lwqzz"><img src="https://avatars.githubusercontent.com/u/62584513?v=4?s=100" width="100px;" alt="lwqzz"/><br /><sub><b>lwqzz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lwqzz" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -426,25 +429,124 @@ Thanks these wonderful people, welcome to join us:
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tomorrowshipyltm"><img src="https://avatars.githubusercontent.com/u/61336903?v=4?s=100" width="100px;" alt="tomorrowshipyltm"/><br /><sub><b>tomorrowshipyltm</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=tomorrowshipyltm" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/15613060203"><img src="https://avatars.githubusercontent.com/u/41351615?v=4?s=100" width="100px;" alt="栗磊"/><br /><sub><b>栗磊</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=15613060203" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Alanxtl"><img src="https://avatars.githubusercontent.com/u/25652981?v=4?s=100" width="100px;" alt="Alan"/><br /><sub><b>Alan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Alanxtl" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.hadoop.wiki/"><img src="https://avatars.githubusercontent.com/u/29418975?v=4?s=100" width="100px;" alt="Jast"/><br /><sub><b>Jast</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zhangshenghang" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zuobiao-zhou"><img src="https://avatars.githubusercontent.com/u/61108539?v=4?s=100" width="100px;" alt="Zhang Yuxuan"/><br /><sub><b>Zhang Yuxuan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zuobiao-zhou" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.hadoop.wiki/"><img src="https://avatars.githubusercontent.com/u/29418975?v=4?s=100" width="100px;" alt="Jast"/><br /><sub><b>Jast</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zhangshenghang" title="Code">💻</a> <a href="#ideas-zhangshenghang" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/apache/hertzbeat/commits?author=zhangshenghang" title="Documentation">📖</a> <a href="#blog-zhangshenghang" title="Blogposts">📝</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Azhangshenghang" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=zhangshenghang" title="Tests">⚠️</a> <a href="#design-zhangshenghang" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zuobiao-zhou"><img src="https://avatars.githubusercontent.com/u/61108539?v=4?s=100" width="100px;" alt="Zhang Yuxuan"/><br /><sub><b>Zhang Yuxuan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zuobiao-zhou" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=zuobiao-zhou" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Azuobiao-zhou" title="Bug reports">🐛</a> <a href="#blog-zuobiao-zhou" title="Blogposts">📝</a> <a href="https://github.com/apache/hertzbeat/commits?author=zuobiao-zhou" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Pzz-2021"><img src="https://avatars.githubusercontent.com/u/118056735?v=4?s=100" width="100px;" alt="P.P."/><br /><sub><b>P.P.</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Pzz-2021" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LLP2333"><img src="https://avatars.githubusercontent.com/u/61670545?v=4?s=100" width="100px;" alt="llp2333"/><br /><sub><b>llp2333</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LLP2333" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/HeartLinked"><img src="https://avatars.githubusercontent.com/u/78212101?v=4?s=100" width="100px;" alt="feiyang li"/><br /><sub><b>feiyang li</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=HeartLinked" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Aias00"><img src="https://avatars.githubusercontent.com/u/25810623?v=4?s=100" width="100px;" alt="aias00"/><br /><sub><b>aias00</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Aias00" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Aias00"><img src="https://avatars.githubusercontent.com/u/25810623?v=4?s=100" width="100px;" alt="aias00"/><br /><sub><b>aias00</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Aias00" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Aias00" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AAias00" title="Bug reports">🐛</a> <a href="#ideas-Aias00" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/apache/hertzbeat/commits?author=Aias00" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/westboy"><img src="https://avatars.githubusercontent.com/u/6385565?v=4?s=100" width="100px;" alt="Jin"/><br /><sub><b>Jin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=westboy" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.csdn.net/qq_52397471"><img src="https://avatars.githubusercontent.com/u/77964041?v=4?s=100" width="100px;" alt="YuLuo"/><br /><sub><b>YuLuo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yuluo-yx" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Yanshuming1"><img src="https://avatars.githubusercontent.com/u/118667222?v=4?s=100" width="100px;" alt="xiaoming student"/><br /><sub><b>xiaoming student</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Yanshuming1" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.csdn.net/qq_52397471"><img src="https://avatars.githubusercontent.com/u/77964041?v=4?s=100" width="100px;" alt="YuLuo"/><br /><sub><b>YuLuo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yuluo-yx" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Ayuluo-yx" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=yuluo-yx" title="Tests">⚠️</a> <a href="#blog-yuluo-yx" title="Blogposts">📝</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Yanshuming1"><img src="https://avatars.githubusercontent.com/u/118667222?v=4?s=100" width="100px;" alt="linDong"/><br /><sub><b>linDong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Yanshuming1" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Yanshuming1" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AYanshuming1" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lwjxy"><img src="https://avatars.githubusercontent.com/u/52726400?v=4?s=100" width="100px;" alt="lwjxy"/><br /><sub><b>lwjxy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lwjxy" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://thespica.github.io/"><img src="https://avatars.githubusercontent.com/u/119573640?v=4?s=100" width="100px;" alt="John"/><br /><sub><b>John</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Thespica" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://thespica.github.io/"><img src="https://avatars.githubusercontent.com/u/119573640?v=4?s=100" width="100px;" alt="John"/><br /><sub><b>John</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Thespica" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Thespica" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/boatrainlsz"><img src="https://avatars.githubusercontent.com/u/18243785?v=4?s=100" width="100px;" alt="boatrainlsz"/><br /><sub><b>boatrainlsz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=boatrainlsz" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.yitianyigexiangfa.com/"><img src="https://avatars.githubusercontent.com/u/3973419?v=4?s=100" width="100px;" alt="Bill Lau"/><br /><sub><b>Bill Lau</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=JavaProgrammerLB" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lw-yang"><img src="https://avatars.githubusercontent.com/u/23456873?v=4?s=100" width="100px;" alt="lwyang"/><br /><sub><b>lwyang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lw-yang" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xfl12345"><img src="https://avatars.githubusercontent.com/u/17960863?v=4?s=100" width="100px;" alt="xfl12345"/><br /><sub><b>xfl12345</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xfl12345" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yykaue"><img src="https://avatars.githubusercontent.com/u/22905143?v=4?s=100" width="100px;" alt="Limbo"/><br /><sub><b>Limbo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yykaue" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/irenhongyan"><img src="https://avatars.githubusercontent.com/u/53438321?v=4?s=100" width="100px;" alt="哈哈哈哈哈哈哈哈哈"/><br /><sub><b>哈哈哈哈哈哈哈哈哈</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=irenhongyan" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ileonli"><img src="https://avatars.githubusercontent.com/u/45332412?v=4?s=100" width="100px;" alt="Leon Li"/><br /><sub><b>Leon Li</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ileonli" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://fnil.net/"><img src="https://avatars.githubusercontent.com/u/14142?v=4?s=100" width="100px;" alt="dennis zhuang"/><br /><sub><b>dennis zhuang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=killme2008" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kerwin612"><img src="https://avatars.githubusercontent.com/u/3371163?v=4?s=100" width="100px;" alt="Kerwin Bryant"/><br /><sub><b>Kerwin Bryant</b></sub></a><br /><a href="#design-kerwin612" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/commits?author=kerwin612" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=kerwin612" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Akerwin612" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ShineDevelopment"><img src="https://avatars.githubusercontent.com/u/59306780?v=4?s=100" width="100px;" alt="daixianglong"/><br /><sub><b>daixianglong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ShineDevelopment" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mchgood"><img src="https://avatars.githubusercontent.com/u/38482005?v=4?s=100" width="100px;" alt="mchgood"/><br /><sub><b>mchgood</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mchgood" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pwallk"><img src="https://avatars.githubusercontent.com/u/69385076?v=4?s=100" width="100px;" alt="kangli"/><br /><sub><b>kangli</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pwallk" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=pwallk" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Apwallk" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cdphantom"><img src="https://avatars.githubusercontent.com/u/12674795?v=4?s=100" width="100px;" alt="cdphantom"/><br /><sub><b>cdphantom</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=cdphantom" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/asd108908382"><img src="https://avatars.githubusercontent.com/u/77717999?v=4?s=100" width="100px;" alt="jiawei.guo"/><br /><sub><b>jiawei.guo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=asd108908382" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/QBH-insist"><img src="https://avatars.githubusercontent.com/u/39401478?v=4?s=100" width="100px;" alt="QBH-insist"/><br /><sub><b>QBH-insist</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=QBH-insist" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jiangsh-ui"><img src="https://avatars.githubusercontent.com/u/86990361?v=4?s=100" width="100px;" alt="jiangsh"/><br /><sub><b>jiangsh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jiangsh-ui" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/keaifafafa"><img src="https://avatars.githubusercontent.com/u/83876361?v=4?s=100" width="100px;" alt="Keaifa"/><br /><sub><b>Keaifa</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=keaifafafa" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Akeaifafafa" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/loong95"><img src="https://avatars.githubusercontent.com/u/16333958?v=4?s=100" width="100px;" alt="Loong"/><br /><sub><b>Loong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=loong95" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ceekay47"><img src="https://avatars.githubusercontent.com/u/104664857?v=4?s=100" width="100px;" alt="Chandrakant Vankayalapati"/><br /><sub><b>Chandrakant Vankayalapati</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ceekay47" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MRgenial"><img src="https://avatars.githubusercontent.com/u/49973336?v=4?s=100" width="100px;" alt="b_mountain"/><br /><sub><b>b_mountain</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MRgenial" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TemirlanBasitov"><img src="https://avatars.githubusercontent.com/u/57500808?v=4?s=100" width="100px;" alt="TemirlanBasitov"/><br /><sub><b>TemirlanBasitov</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=TemirlanBasitov" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wyfvsfy"><img src="https://avatars.githubusercontent.com/u/11973517?v=4?s=100" width="100px;" alt="wyfvsfy"/><br /><sub><b>wyfvsfy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wyfvsfy" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sherry-peng2333"><img src="https://avatars.githubusercontent.com/u/70619577?v=4?s=100" width="100px;" alt="sherry-peng2333"/><br /><sub><b>sherry-peng2333</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=sherry-peng2333" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lixiaobaivv"><img src="https://avatars.githubusercontent.com/u/39290771?v=4?s=100" width="100px;" alt="Yzzz"/><br /><sub><b>Yzzz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lixiaobaivv" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.bckf.cn/"><img src="https://avatars.githubusercontent.com/u/13309008?v=4?s=100" width="100px;" alt="puruidong"/><br /><sub><b>puruidong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pruidong" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shinestare"><img src="https://avatars.githubusercontent.com/u/13570619?v=4?s=100" width="100px;" alt="shinestare"/><br /><sub><b>shinestare</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=shinestare" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/po-168"><img src="https://avatars.githubusercontent.com/u/185745593?v=4?s=100" width="100px;" alt="po-168"/><br /><sub><b>po-168</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=po-168" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/All-The-Best-for"><img src="https://avatars.githubusercontent.com/u/76414672?v=4?s=100" width="100px;" alt="wbs99"/><br /><sub><b>wbs99</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=All-The-Best-for" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/starryCoder"><img src="https://avatars.githubusercontent.com/u/46510059?v=4?s=100" width="100px;" alt="starryCoder"/><br /><sub><b>starryCoder</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=starryCoder" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hasimmollah"><img src="https://avatars.githubusercontent.com/u/32538599?v=4?s=100" width="100px;" alt="hasimmollah"/><br /><sub><b>hasimmollah</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=hasimmollah" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ayu-v0"><img src="https://avatars.githubusercontent.com/u/127600988?v=4?s=100" width="100px;" alt="Ayu"/><br /><sub><b>Ayu</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ayu-v0" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Rancho-7"><img src="https://avatars.githubusercontent.com/u/59016860?v=4?s=100" width="100px;" alt="Nick Guo"/><br /><sub><b>Nick Guo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Rancho-7" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=Rancho-7" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ARancho-7" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/doveLin0818"><img src="https://avatars.githubusercontent.com/u/190927907?v=4?s=100" width="100px;" alt="doveLin"/><br /><sub><b>doveLin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=doveLin0818" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://zzrl.cc/"><img src="https://avatars.githubusercontent.com/u/91836599?v=4?s=100" width="100px;" alt="yunfan24"/><br /><sub><b>yunfan24</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yunfan24" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=yunfan24" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Ayunfan24" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=yunfan24" title="Tests">⚠️</a> <a href="#blog-yunfan24" title="Blogposts">📝</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lctking"><img src="https://avatars.githubusercontent.com/u/168249998?v=4?s=100" width="100px;" alt="nullwli"/><br /><sub><b>nullwli</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lctking" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://simonsigre.com/"><img src="https://avatars.githubusercontent.com/u/14932913?v=4?s=100" width="100px;" alt="Simon Sigré"/><br /><sub><b>Simon Sigré</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=simonsigre" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=simonsigre" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.ponfee.cn/"><img src="https://avatars.githubusercontent.com/u/46117331?v=4?s=100" width="100px;" alt="ponfee"/><br /><sub><b>ponfee</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ponfee" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Vedant7789"><img src="https://avatars.githubusercontent.com/u/147625492?v=4?s=100" width="100px;" alt="Vedant7789"/><br /><sub><b>Vedant7789</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Vedant7789" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Craaaaazy77"><img src="https://avatars.githubusercontent.com/u/23025522?v=4?s=100" width="100px;" alt="Craaaaazy77"/><br /><sub><b>Craaaaazy77</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Craaaaazy77" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Suvrat1629"><img src="https://avatars.githubusercontent.com/u/140749446?v=4?s=100" width="100px;" alt="Suvrat1629"/><br /><sub><b>Suvrat1629</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Suvrat1629" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://ghyghoo8.github.io/"><img src="https://avatars.githubusercontent.com/u/363129?v=4?s=100" width="100px;" alt="ghy"/><br /><sub><b>ghy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ghyghoo8" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/helei1030"><img src="https://avatars.githubusercontent.com/u/11839080?v=4?s=100" width="100px;" alt="helei1030"/><br /><sub><b>helei1030</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=helei1030" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://medium.com/@pjfanning"><img src="https://avatars.githubusercontent.com/u/11783444?v=4?s=100" width="100px;" alt="PJ Fanning"/><br /><sub><b>PJ Fanning</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pjfanning" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Apjfanning" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=pjfanning" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MonsterChenzhuo"><img src="https://avatars.githubusercontent.com/u/60029759?v=4?s=100" width="100px;" alt="monster"/><br /><sub><b>monster</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MonsterChenzhuo" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MasamiYui"><img src="https://avatars.githubusercontent.com/u/22274133?v=4?s=100" width="100px;" alt="Sherlock Yin"/><br /><sub><b>Sherlock Yin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MasamiYui" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=MasamiYui" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AMasamiYui" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wanhao23"><img src="https://avatars.githubusercontent.com/u/29560961?v=4?s=100" width="100px;" alt="wanhao"/><br /><sub><b>wanhao</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wanhao23" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=wanhao23" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jonasHanhan"><img src="https://avatars.githubusercontent.com/u/130035609?v=4?s=100" width="100px;" alt="jonasHanhan"/><br /><sub><b>jonasHanhan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jonasHanhan" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/NikhilMurugesan"><img src="https://avatars.githubusercontent.com/u/49281792?v=4?s=100" width="100px;" alt="NikhilMurugesan"/><br /><sub><b>NikhilMurugesan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=NikhilMurugesan" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/myangle1120"><img src="https://avatars.githubusercontent.com/u/19237013?v=4?s=100" width="100px;" alt="myangle1120"/><br /><sub><b>myangle1120</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=myangle1120" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yasminvo"><img src="https://avatars.githubusercontent.com/u/107528848?v=4?s=100" width="100px;" alt="yasminvo"/><br /><sub><b>yasminvo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yasminvo" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/notbugggg"><img src="https://avatars.githubusercontent.com/u/147966331?v=4?s=100" width="100px;" alt="不关银渐层的事哦"/><br /><sub><b>不关银渐层的事哦</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=notbugggg" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Anotbugggg" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yyahang"><img src="https://avatars.githubusercontent.com/u/90464876?v=4?s=100" width="100px;" alt="yyahang"/><br /><sub><b>yyahang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yyahang" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JuJinPark"><img src="https://avatars.githubusercontent.com/u/44892459?v=4?s=100" width="100px;" alt="jujin"/><br /><sub><b>jujin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=JuJinPark" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=JuJinPark" title="Documentation">📖</a> <a href="#ideas-JuJinPark" title="Ideas, Planning, & Feedback">🤔</a> <a href="#blog-JuJinPark" title="Blogposts">📝</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LL-LIN"><img src="https://avatars.githubusercontent.com/u/43002118?v=4?s=100" width="100px;" alt="LL-LIN"/><br /><sub><b>LL-LIN</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LL-LIN" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ALL-LIN" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://bigcyy.github.io/"><img src="https://avatars.githubusercontent.com/u/73413979?v=4?s=100" width="100px;" alt="Yang Chen"/><br /><sub><b>Yang Chen</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=bigcyy" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=bigcyy" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Abigcyy" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sarthakeash"><img src="https://avatars.githubusercontent.com/u/74091160?v=4?s=100" width="100px;" alt="Sarthak Arora"/><br /><sub><b>Sarthak Arora</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=sarthakeash" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=sarthakeash" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PengJingzhao"><img src="https://avatars.githubusercontent.com/u/97368949?v=4?s=100" width="100px;" alt="彭镜肇"/><br /><sub><b>彭镜肇</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=PengJingzhao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gagaradio"><img src="https://avatars.githubusercontent.com/u/18532370?v=4?s=100" width="100px;" alt="Walter Jia"/><br /><sub><b>Walter Jia</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=gagaradio" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/boyucjz"><img src="https://avatars.githubusercontent.com/u/18730041?v=4?s=100" width="100px;" alt="boyucjz"/><br /><sub><b>boyucjz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=boyucjz" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Cyanty"><img src="https://avatars.githubusercontent.com/u/153884653?v=4?s=100" width="100px;" alt="Cyanty"/><br /><sub><b>Cyanty</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Cyanty" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Cyanty" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KevinLLF"><img src="https://avatars.githubusercontent.com/u/85452733?v=4?s=100" width="100px;" alt="Jay丿167"/><br /><sub><b>Jay丿167</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=KevinLLF" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Duansg"><img src="https://avatars.githubusercontent.com/u/112607719?v=4?s=100" width="100px;" alt="Duansg"/><br /><sub><b>Duansg</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Duansg" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xiaomizhou2"><img src="https://avatars.githubusercontent.com/u/47807926?v=4?s=100" width="100px;" alt="zhangyaxi"/><br /><sub><b>zhangyaxi</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xiaomizhou2" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=xiaomizhou2" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RainBondsongyg"><img src="https://avatars.githubusercontent.com/u/94501396?v=4?s=100" width="100px;" alt="songyg"/><br /><sub><b>songyg</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=RainBondsongyg" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lx1229"><img src="https://avatars.githubusercontent.com/u/44620005?v=4?s=100" width="100px;" alt="Liuxin"/><br /><sub><b>Liuxin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lx1229" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yy549159265"><img src="https://avatars.githubusercontent.com/u/40821310?v=4?s=100" width="100px;" alt="yy549159265"/><br /><sub><b>yy549159265</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yy549159265" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=yy549159265" title="Tests">⚠️</a> <a href="#design-yy549159265" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cto-huhang"><img src="https://avatars.githubusercontent.com/u/53338629?v=4?s=100" width="100px;" alt="cto-huhang"/><br /><sub><b>cto-huhang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=cto-huhang" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Saramanda9988"><img src="https://avatars.githubusercontent.com/u/176664901?v=4?s=100" width="100px;" alt="LunaRain_079"/><br /><sub><b>LunaRain_079</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Saramanda9988" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/delei"><img src="https://avatars.githubusercontent.com/u/17263766?v=4?s=100" width="100px;" alt="DeleiGuo"/><br /><sub><b>DeleiGuo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=delei" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=delei" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=delei" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Adelei" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/chingjustwe"><img src="https://avatars.githubusercontent.com/u/13643747?v=4?s=100" width="100px;" alt="Rocky, Chi"/><br /><sub><b>Rocky, Chi</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=chingjustwe" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rowankid"><img src="https://avatars.githubusercontent.com/u/18652781?v=4?s=100" width="100px;" alt="Wenqi Luo"/><br /><sub><b>Wenqi Luo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/issues?q=author%3Arowankid" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tuzuy"><img src="https://avatars.githubusercontent.com/u/95274591?v=4?s=100" width="100px;" alt="tuzuy"/><br /><sub><b>tuzuy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=tuzuy" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/carlpinto25"><img src="https://avatars.githubusercontent.com/u/117299909?v=4?s=100" width="100px;" alt="carl pinto"/><br /><sub><b>carl pinto</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=carlpinto25" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://cxhello.top/"><img src="https://avatars.githubusercontent.com/u/49056040?v=4?s=100" width="100px;" alt="cxhello"/><br /><sub><b>cxhello</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=cxhello" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jl15988"><img src="https://avatars.githubusercontent.com/u/70638770?v=4?s=100" width="100px;" alt="会功夫的李白"/><br /><sub><b>会功夫的李白</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jl15988" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.aytop.cloud/"><img src="https://avatars.githubusercontent.com/u/37127008?v=4?s=100" width="100px;" alt="Albert.Yang"/><br /><sub><b>Albert.Yang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=AlbertYang0801" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.tokenlen.top/"><img src="https://avatars.githubusercontent.com/u/150590575?v=4?s=100" width="100px;" alt="zhou yong kang"/><br /><sub><b>zhou yong kang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mengnankkkk" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/warrobe"><img src="https://avatars.githubusercontent.com/u/89446159?v=4?s=100" width="100px;" alt="warrobe"/><br /><sub><b>warrobe</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=warrobe" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Jetiaime"><img src="https://avatars.githubusercontent.com/u/93769000?v=4?s=100" width="100px;" alt="TeAmo"/><br /><sub><b>TeAmo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Jetiaime" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pentium100"><img src="https://avatars.githubusercontent.com/u/27917?v=4?s=100" width="100px;" alt="pentium100"/><br /><sub><b>pentium100</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pentium100" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dedyks"><img src="https://avatars.githubusercontent.com/u/23741665?v=4?s=100" width="100px;" alt="Dedy Kurniawan Santoso"/><br /><sub><b>Dedy Kurniawan Santoso</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=dedyks" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -462,17 +564,17 @@ Thanks these wonderful people, welcome to join us:
|
||||
|
||||
[Chat On Discord](https://discord.gg/Fb6M73htGr)
|
||||
|
||||
微信交流群 : 加 `tan-cloud` 好友邀请进群.
|
||||
微信交流群 : 加 `ahertzbeat` 好友邀请进群.
|
||||
|
||||
微信公众号 : 搜索 ID `usthecom`.
|
||||
|
||||
[QQ交流群](https://jq.qq.com/?_wv=1027&k=Bud9OzdI) : 群号 `630061200`
|
||||
[QQ交流群](https://qm.qq.com/q/xxqecSC2cw) : 群号 `1035688434`
|
||||
|
||||
[Github Discussion](https://github.com/apache/hertzbeat/discussions)
|
||||
|
||||
[Reddit Community](https://www.reddit.com/r/hertzbeat/)
|
||||
|
||||
[Follow Us Twitter](https://twitter.com/hertzbeat1024)
|
||||
[Follow Us Twitter](https://x.com/hertzbeat1024)
|
||||
|
||||
[Subscribe YouTube](https://www.youtube.com/channel/UCri75zfWX0GHqJFPENEbLow)
|
||||
|
||||
@@ -490,15 +592,9 @@ HertzBeat is built on so many great open source projects, thanks to them!
|
||||
<p align="left">
|
||||
<img src="./home/static/img/home/cncf-landscape-left-logo.svg" width="300"> <img src="./home/static/img/home/cncf-right-logo.svg" width="345" />
|
||||
<br /><br />
|
||||
HertzBeat has been included in the <a href="https://landscape.cncf.io/guide?item=observability-and-analysis--monitoring--hertzbeat#observability-and-analysis--monitoring">
|
||||
CNCF Observability And Analysis - Monitoring Landscape.</a>
|
||||
HertzBeat has been included in the <a href="https://landscape.cncf.io/?item=observability-and-analysis--observability--hertzbeat">
|
||||
CNCF Observability And Analysis - Observability Landscape.</a>
|
||||
</p>
|
||||
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#apache/hertzbeat&Date)
|
||||
|
||||
|
||||
## 🛡️ License
|
||||
[`Apache License, Version 2.0`](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
|
||||
+604
@@ -0,0 +1,604 @@
|
||||
<p align="center">
|
||||
<a href="https://hertzbeat.apache.org">
|
||||
<img alt="hertzbeat" src="/home/static/img/hertzbeat-brand.svg" width="260">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<b>Readme</b>:
|
||||
<a href="README.md">English</a> | <a href="README_CN.md">中文</a> | <b><a href="README_JP.md">日本語</a></b>
|
||||
</p>
|
||||
|
||||
> オープンソースのリアルタイム監視システム、エージェントレス、クラスタ対応、Prometheus互換、カスタムおよびステータスページ付き。
|
||||
|
||||
[](https://discord.gg/Fb6M73htGr)
|
||||
[](https://www.reddit.com/r/hertzbeat/)
|
||||
[](https://x.com/hertzbeat1024)
|
||||
[](https://www.bestpractices.dev/projects/8139)
|
||||
[](https://app.codecov.io/gh/apache/hertzbeat)
|
||||
[](https://hub.docker.com/r/apache/hertzbeat)
|
||||
[](https://artifacthub.io/packages/search?repo=hertzbeat)
|
||||
[](https://www.youtube.com/channel/UCri75zfWX0GHqJFPENEbLow)
|
||||
[](https://gitpod.io/#https://github.com/apache/hertzbeat)
|
||||
|
||||
|
||||
**公式サイト: [hertzbeat.apache.org](https://hertzbeat.apache.org)**
|
||||
**メール:** メーリングリストに登録するために、<a href="mailto:dev-subscribe@hertzbeat.apache.org">dev-subscribe@hertzbeat.apache.org</a>にメールを送ってください。
|
||||
|
||||
|
||||
## 🎡 <font color="green">紹介</font>
|
||||
|
||||
[Apache HertzBeat](https://github.com/apache/hertzbeat) (incubating)はオープンソースのリアルタイム監視システム、エージェントレス、クラスタ対応、Prometheus互換、カスタムおよびステータスページ付き。
|
||||
|
||||
### 特性
|
||||
|
||||
- **モニター・アラーム・通知** を一体化し、Web、データベース、キャッシュ、OS、ミドルウェア、クラウドネイティブ、ネットワークなどをサポート。
|
||||
- 使いやすさを重視し、マウスのクリックだけで完全なWebベースの操作が可能。
|
||||
- `Http、Jmx、Ssh、Snmp、jdbc、Prometheus`などのプロトコルを設定可能にし、テンプレート`YML`ファイルをオンラインで設定するだけで、あらゆるメトリクスを収集できるようにします。HertzBeatでオンライン設定するだけで、`K8S`や`Docker`のような新しい監視タイプに素早く対応できることを想像してみてください。
|
||||
- `Prometheus`のエコシステム互換、Prometheusが監視できるものを、全てオンライン設定によって監視することができます。
|
||||
- 高性能で、コレクタークラスタ、マルチアイソレートネットワーク、クラウドエッジをサポート。
|
||||
- 柔軟なアラーム閾値ルールで、メール、discord、slack、telegramなどを通じてタイムリーに通知。
|
||||
- 強力なステータスページを簡単に構築し、リアルタイムのステータスをユーザーと共有。
|
||||
|
||||
|
||||
> `HertzBeat`の強力なカスタマイズ、多種類のサポート、高性能および容易な拡張性は、ユーザーが迅速に独自の監視システムを構築するのを支援することを目的としています。
|
||||
|
||||
----
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
----
|
||||
|
||||
## 🥐 モジュール
|
||||
|
||||

|
||||
|
||||
## ⛄ 利用可能
|
||||
|
||||
> `mysql`、`Java仮想マシン`、`k8s` などのすべての監視コレクション タイプを `YML` 監視テンプレートとして定義し、ユーザーがそれらをインポートして対応するタイプの監視をサポートできるようにしています。
|
||||
> カスタマイズされた一般的なモニターのテンプレートを提供することを歓迎します。
|
||||
|
||||
- [Website](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-website.yml), [Port Telnet](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-port.yml),
|
||||
[Http Api](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-api.yml), [Ping Connect](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ping.yml),
|
||||
[Jvm](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-jvm.yml), [SiteMap](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-fullsite.yml),
|
||||
[Ssl Certificate](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ssl_cert.yml), [SpringBoot2](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-springboot2.yml),
|
||||
[FTP Server](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ftp.yml), [SpringBoot3](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-springboot3.yml),
|
||||
[Udp Port](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-udp_port.yml), [Dns](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dns.yml),
|
||||
[Pop3](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-pop3.yml), [Ntp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ntp.yml),
|
||||
[Api Code](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-api_code.yml), [Smtp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-smtp.yml),
|
||||
[Nginx](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-nginx.yml)
|
||||
- [Mysql](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mysql.yml), [PostgreSQL](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-postgresql.yml),
|
||||
[MariaDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mariadb.yml), [Redis](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis.yml),
|
||||
[ElasticSearch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-elasticsearch.yml), [SqlServer](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-sqlserver.yml),
|
||||
[Oracle](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-oracle.yml), [MongoDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mongodb.yml),
|
||||
[DM](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dm.yml), [OpenGauss](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-opengauss.yml),
|
||||
[ClickHouse](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-clickhouse.yml), [IoTDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-iotdb.yml),
|
||||
[Redis Cluster](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis_cluster.yml), [Redis Sentinel](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis_sentinel.yml)
|
||||
[Doris BE](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-doris_be.yml), [Doris FE](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-doris_fe.yml),
|
||||
[Memcached](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-memcached.yml), [NebulaGraph](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-nebula_graph.yml)
|
||||
- [Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-linux.yml), [Ubuntu](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ubuntu.yml),
|
||||
[CentOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-centos.yml), [Windows](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-windows.yml),
|
||||
[EulerOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-euleros.yml), [Fedora CoreOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-coreos.yml),
|
||||
[OpenSUSE](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-opensuse.yml), [Rocky Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-rockylinux.yml),
|
||||
[Red Hat](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redhat.yml), [FreeBSD](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-freebsd.yml),
|
||||
[AlmaLinux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-almalinux.yml), [Debian Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-debian.yml)
|
||||
- [Tomcat](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-tomcat.yml), [Nacos](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-nacos.yml),
|
||||
[Zookeeper](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-zookeeper.yml), [RabbitMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-rabbitmq.yml),
|
||||
[Flink](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-flink.yml), [Kafka](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-kafka.yml),
|
||||
[ShenYu](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-shenyu.yml), [DynamicTp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dynamic_tp.yml),
|
||||
[Jetty](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-jetty.yml), [ActiveMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-activemq.yml),
|
||||
[Spring Gateway](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-spring_gateway.yml), [EMQX MQTT](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-emqx.yml),
|
||||
[AirFlow](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-airflow.yml), [Hive](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hive.yml),
|
||||
[Spark](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-spark.yml), [Hadoop](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hadoop.yml)
|
||||
- [Kubernetes](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-kubernetes.yml), [Docker](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-docker.yml)
|
||||
- [CiscoSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-cisco_switch.yml), [HpeSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hpe_switch.yml),
|
||||
[HuaweiSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-huawei_switch.yml), [TpLinkSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-tplink_switch.yml),
|
||||
[H3cSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-h3c_switch.yml)
|
||||
- その他、カスタマイズされたモニターテンプレート。
|
||||
- 通知利用可能 `Discord` `Slack` `Telegram` `Email` `Dingtalk` `WeChat` `FeiShu` `Webhook` `SMS` `ServerChan`。
|
||||
|
||||
## 🐕 クイックスタート
|
||||
|
||||
- HertzBeat をローカルに展開する場合は、以下のデプロイメントドキュメントを参照してください。
|
||||
|
||||
### 🍞 HertzBeatのインストール
|
||||
> HertzBeatは、ソースコードのインストールとブート、Dockerコンテナの実行とインストールパッケージによるインストールとデプロイをサポートし、CPUアーキテクチャはx86/arm64をサポートします。
|
||||
|
||||
##### 方式1:Docker
|
||||
|
||||
1. `docker` で以下の指令を実行します:
|
||||
|
||||
```shell
|
||||
docker run -d -p 1157:1157 -p 1158:1158 --name hertzbeat apache/hertzbeat
|
||||
```
|
||||
|
||||
2. スタート:`http://localhost:4200`にアクセスします。デフォルトのアカウントとパスワード:`admin/hertzbeat`。
|
||||
|
||||
3. コレクタークラスタのデプロイメント(オプション)
|
||||
|
||||
```shell
|
||||
docker run -d -e IDENTITY=custom-collector-name -e MANAGER_HOST=127.0.0.1 -e MANAGER_PORT=1158 --name hertzbeat-collector apache/hertzbeat-collector
|
||||
```
|
||||
|
||||
- `-e IDENTITY=custom-collector-name` :コレクターのユニーク ID。
|
||||
- `-e MODE=public` :実行モード(パブリッククラスタまたはプライベートクラウドエッジ)。
|
||||
- `-e MANAGER_HOST=127.0.0.1` :メインhertzbeatサーバーのIP。
|
||||
- `-e MANAGER_PORT=1158` :メインhertzbeatサーバポート。
|
||||
|
||||
|
||||
詳細ステップ [通过 Docker 方式安装 HertzBeat](https://hertzbeat.apache.org/docs/start/docker-deploy)
|
||||
|
||||
##### 方式2:インストールパッケージ
|
||||
|
||||
1. リリースパッケージをダウンロード `hertzbeat-xx.tar.gz` [Download](https://hertzbeat.apache.org/docs/download)
|
||||
2. HertzBeatのymlファイルを設定 `hertzbeat/config/application.yml` (オプション)
|
||||
3. コマンド`$ ./bin/startup.sh`または`bin/startup.bat`を実行
|
||||
4. スタート:`http://localhost:4200`にアクセスします。デフォルトのアカウントとパスワード:`admin/hertzbeat`
|
||||
5. コレクタークラスタのデプロイメント(オプション)
|
||||
- コレクターパッケージを別のホストにダウンロード `hertzbeat-collector-xx.tar.gz` [Download](https://hertzbeat.apache.org/docs/download)
|
||||
- コレクターのymlファイルを設定 `hertzbeat-collector/config/application.yml`
|
||||
```yaml
|
||||
collector:
|
||||
dispatch:
|
||||
entrance:
|
||||
netty:
|
||||
enabled: true
|
||||
identity: ${IDENTITY:}
|
||||
mode: ${MODE:public}
|
||||
manager-host: ${MANAGER_HOST:127.0.0.1}
|
||||
manager-port: ${MANAGER_PORT:1158}
|
||||
```
|
||||
- `identity: ${IDENTITY:}`:コレクターのユニークID。
|
||||
- `mode: ${MODE:public}`:実行モード(パブリッククラスタまたはプライベートクラウドエッジ)。
|
||||
- `manager-host: ${MANAGER_HOST:127.0.0.1}`:メインhertzbeatサーバーのIP。
|
||||
- `manager-port: ${MANAGER_PORT:1158}`:メインhertzbeatサーバポート。
|
||||
- コマンド`$ ./bin/startup.sh`または`bin/startup.bat`を実行。
|
||||
- `http://localhost:1157`にアクセスし、登録された新しいコレクターを見ることがでます。
|
||||
|
||||
詳細ステップ [通过安装包安装HertzBeat](https://hertzbeat.apache.org/docs/start/package-deploy)
|
||||
|
||||
##### 方式3:ローカルの実行
|
||||
|
||||
1. ローカルの実行には、バックエンドのプロジェクト`manager`とフロントエンドのプロジェクト`web-app`を起動する必要があります。
|
||||
2. バックエンド:`maven3+`、`java17`と`lombok`の環境は必要です。`YML` 設定を修正し、Java仮想マシンパラメータに`--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED` を追加し、`manager` を起動します。
|
||||
3. フロントエンド:`nodejs npm angular-cli`の環境は必要です。ローカルのバックエンドが立ち上がったら、`web-app` ディレクトリで `ng serve --open` というコマンドを実行します。
|
||||
4. スタート:`http://localhost:4200`にアクセスします。デフォルトのアカウントとパスワード:`admin/hertzbeat`。
|
||||
|
||||
詳細ステップ [貢献ガイド](CONTRIBUTING.md)
|
||||
|
||||
##### 方式4:Docker-Compose
|
||||
|
||||
[Docker-Compose 部署脚本](script/docker-compose)でpostgresql/mysqlデータベース、victoria-metrics、iotdb、またはtdengine時系列データベースとHertzbeat一括デプロイ。
|
||||
|
||||
詳細ステップ [通过 Docker-Compose 安装 HertzBeat](script/docker-compose/README.md)
|
||||
|
||||
##### 方式5:Kubernetes Helm Charts
|
||||
|
||||
Helm ChartでHertzBeatクラスタコンポーネントをKubernetesクラスタに一括デプロイ。
|
||||
|
||||
詳細ステップ [Artifact Hub](https://artifacthub.io/packages/helm/hertzbeat/hertzbeat)
|
||||
|
||||
**HAVE FUN**
|
||||
|
||||
## ✨ Contributors
|
||||
|
||||
Thanks these wonderful people, welcome to join us:
|
||||
[貢献ガイド](CONTRIBUTING.md)
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tomsun28"><img src="https://avatars.githubusercontent.com/u/24788200?v=4?s=100" width="100px;" alt="tomsun28"/><br /><sub><b>tomsun28</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=tomsun28" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=tomsun28" title="Documentation">📖</a> <a href="#design-tomsun28" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wang1027-wqh"><img src="https://avatars.githubusercontent.com/u/71161318?v=4?s=100" width="100px;" alt="会编程的王学长"/><br /><sub><b>会编程的王学长</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wang1027-wqh" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=wang1027-wqh" title="Documentation">📖</a> <a href="#design-wang1027-wqh" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.maxkey.top/"><img src="https://avatars.githubusercontent.com/u/1563377?v=4?s=100" width="100px;" alt="MaxKey"/><br /><sub><b>MaxKey</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=shimingxy" title="Code">💻</a> <a href="#design-shimingxy" title="Design">🎨</a> <a href="#ideas-shimingxy" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.gcdd.top/"><img src="https://avatars.githubusercontent.com/u/26523525?v=4?s=100" width="100px;" alt="观沧海"/><br /><sub><b>观沧海</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=gcdd1993" title="Code">💻</a> <a href="#design-gcdd1993" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Agcdd1993" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/a25017012"><img src="https://avatars.githubusercontent.com/u/32265356?v=4?s=100" width="100px;" alt="yuye"/><br /><sub><b>yuye</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=a25017012" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=a25017012" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jx10086"><img src="https://avatars.githubusercontent.com/u/5323228?v=4?s=100" width="100px;" alt="jx10086"/><br /><sub><b>jx10086</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jx10086" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Ajx10086" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/winnerTimer"><img src="https://avatars.githubusercontent.com/u/76024658?v=4?s=100" width="100px;" alt="winnerTimer"/><br /><sub><b>winnerTimer</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=winnerTimer" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AwinnerTimer" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/goo-kits"><img src="https://avatars.githubusercontent.com/u/13163673?v=4?s=100" width="100px;" alt="goo-kits"/><br /><sub><b>goo-kits</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=goo-kits" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Agoo-kits" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/brave4Time"><img src="https://avatars.githubusercontent.com/u/105094014?v=4?s=100" width="100px;" alt="brave4Time"/><br /><sub><b>brave4Time</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=brave4Time" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Abrave4Time" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/walkerlee-lab"><img src="https://avatars.githubusercontent.com/u/8426753?v=4?s=100" width="100px;" alt="WalkerLee"/><br /><sub><b>WalkerLee</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=walkerlee-lab" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Awalkerlee-lab" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fullofjoy"><img src="https://avatars.githubusercontent.com/u/30247571?v=4?s=100" width="100px;" alt="jianghang"/><br /><sub><b>jianghang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=fullofjoy" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Afullofjoy" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ChineseTony"><img src="https://avatars.githubusercontent.com/u/24618786?v=4?s=100" width="100px;" alt="ChineseTony"/><br /><sub><b>ChineseTony</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ChineseTony" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AChineseTony" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wyt199905"><img src="https://avatars.githubusercontent.com/u/85098809?v=4?s=100" width="100px;" alt="wyt199905"/><br /><sub><b>wyt199905</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wyt199905" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/weifuqing"><img src="https://avatars.githubusercontent.com/u/13931013?v=4?s=100" width="100px;" alt="卫傅庆"/><br /><sub><b>卫傅庆</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=weifuqing" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Aweifuqing" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zklmcookle"><img src="https://avatars.githubusercontent.com/u/107192352?v=4?s=100" width="100px;" alt="zklmcookle"/><br /><sub><b>zklmcookle</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zklmcookle" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DevilX5"><img src="https://avatars.githubusercontent.com/u/13269921?v=4?s=100" width="100px;" alt="DevilX5"/><br /><sub><b>DevilX5</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=DevilX5" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=DevilX5" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/djzeng"><img src="https://avatars.githubusercontent.com/u/14074864?v=4?s=100" width="100px;" alt="tea"/><br /><sub><b>tea</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=djzeng" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yangshihui"><img src="https://avatars.githubusercontent.com/u/28550208?v=4?s=100" width="100px;" alt="yangshihui"/><br /><sub><b>yangshihui</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yangshihui" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Ayangshihui" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DreamGirl524"><img src="https://avatars.githubusercontent.com/u/81132838?v=4?s=100" width="100px;" alt="DreamGirl524"/><br /><sub><b>DreamGirl524</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=DreamGirl524" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=DreamGirl524" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gzwlly"><img src="https://avatars.githubusercontent.com/u/83171907?v=4?s=100" width="100px;" alt="gzwlly"/><br /><sub><b>gzwlly</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=gzwlly" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cuipiheqiuqiu"><img src="https://avatars.githubusercontent.com/u/76642201?v=4?s=100" width="100px;" alt="cuipiheqiuqiu"/><br /><sub><b>cuipiheqiuqiu</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=cuipiheqiuqiu" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=cuipiheqiuqiu" title="Tests">⚠️</a> <a href="#design-cuipiheqiuqiu" title="Design">🎨</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/oyiyou"><img src="https://avatars.githubusercontent.com/u/39228891?v=4?s=100" width="100px;" alt="lambert"/><br /><sub><b>lambert</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=oyiyou" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://mroldx.xyz/"><img src="https://avatars.githubusercontent.com/u/34847828?v=4?s=100" width="100px;" alt="mroldx"/><br /><sub><b>mroldx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mroldx" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/woshiniusange"><img src="https://avatars.githubusercontent.com/u/91513022?v=4?s=100" width="100px;" alt="woshiniusange"/><br /><sub><b>woshiniusange</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=woshiniusange" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://vampireachao.github.io/"><img src="https://avatars.githubusercontent.com/u/52746628?v=4?s=100" width="100px;" alt="VampireAchao"/><br /><sub><b>VampireAchao</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=VampireAchao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Ceilzcx"><img src="https://avatars.githubusercontent.com/u/48920254?v=4?s=100" width="100px;" alt="zcx"/><br /><sub><b>zcx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Ceilzcx" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ACeilzcx" title="Bug reports">🐛</a> <a href="#design-Ceilzcx" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/commits?author=Ceilzcx" title="Tests">⚠️</a> <a href="#blog-Ceilzcx" title="Blogposts">📝</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/CharlieXCL"><img src="https://avatars.githubusercontent.com/u/91540487?v=4?s=100" width="100px;" alt="CharlieXCL"/><br /><sub><b>CharlieXCL</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=CharlieXCL" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Privauto"><img src="https://avatars.githubusercontent.com/u/36581456?v=4?s=100" width="100px;" alt="Privauto"/><br /><sub><b>Privauto</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Privauto" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Privauto" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/emrys-he"><img src="https://avatars.githubusercontent.com/u/5848915?v=4?s=100" width="100px;" alt="emrys"/><br /><sub><b>emrys</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=emrys-he" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SxLiuYu"><img src="https://avatars.githubusercontent.com/u/95198625?v=4?s=100" width="100px;" alt="SxLiuYu"/><br /><sub><b>SxLiuYu</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/issues?q=author%3ASxLiuYu" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://allcontributors.org"><img src="https://avatars.githubusercontent.com/u/46410174?v=4?s=100" width="100px;" alt="All Contributors"/><br /><sub><b>All Contributors</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=all-contributors" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gxc-myh"><img src="https://avatars.githubusercontent.com/u/85919258?v=4?s=100" width="100px;" alt="铁甲小宝"/><br /><sub><b>铁甲小宝</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=gxc-myh" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=gxc-myh" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/click33"><img src="https://avatars.githubusercontent.com/u/36243476?v=4?s=100" width="100px;" alt="click33"/><br /><sub><b>click33</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=click33" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://jpom.io/"><img src="https://avatars.githubusercontent.com/u/16408873?v=4?s=100" width="100px;" alt="蒋小小"/><br /><sub><b>蒋小小</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=bwcx-jzy" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.zhihu.com/people/kevinbauer"><img src="https://avatars.githubusercontent.com/u/28581579?v=4?s=100" width="100px;" alt="Kevin Huang"/><br /><sub><b>Kevin Huang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=kevinhuangwl" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TJxiaobao"><img src="https://avatars.githubusercontent.com/u/85919258?v=4?s=100" width="100px;" alt="铁甲小宝"/><br /><sub><b>铁甲小宝</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/issues?q=author%3ATJxiaobao" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=TJxiaobao" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=TJxiaobao" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=TJxiaobao" title="Tests">⚠️</a> <a href="#design-TJxiaobao" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Jack-123-power"><img src="https://avatars.githubusercontent.com/u/84333501?v=4?s=100" width="100px;" alt="Captain Jack"/><br /><sub><b>Captain Jack</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Jack-123-power" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/haibo-duan"><img src="https://avatars.githubusercontent.com/u/7974845?v=4?s=100" width="100px;" alt="haibo.duan"/><br /><sub><b>haibo.duan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=haibo-duan" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/commits?author=haibo-duan" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/assassinfym"><img src="https://avatars.githubusercontent.com/u/15188754?v=4?s=100" width="100px;" alt="assassin"/><br /><sub><b>assassin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/issues?q=author%3Aassassinfym" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=assassinfym" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/csyshu"><img src="https://avatars.githubusercontent.com/u/46591658?v=4?s=100" width="100px;" alt="Reverse wind"/><br /><sub><b>Reverse wind</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=csyshu" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/commits?author=csyshu" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/luxx-lq"><img src="https://avatars.githubusercontent.com/u/58515565?v=4?s=100" width="100px;" alt="luxx"/><br /><sub><b>luxx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=luxx-lq" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=100" width="100px;" alt="Ikko Ashimine"/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=eltociear" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zenan08"><img src="https://avatars.githubusercontent.com/u/80514991?v=4?s=100" width="100px;" alt="leizenan"/><br /><sub><b>leizenan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zenan08" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BKing2020"><img src="https://avatars.githubusercontent.com/u/28869121?v=4?s=100" width="100px;" alt="BKing"/><br /><sub><b>BKing</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=BKing2020" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xingshuaiLi"><img src="https://avatars.githubusercontent.com/u/119487588?v=4?s=100" width="100px;" alt="xingshuaiLi"/><br /><sub><b>xingshuaiLi</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xingshuaiLi" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wangke6666"><img src="https://avatars.githubusercontent.com/u/113656595?v=4?s=100" width="100px;" alt="wangke6666"/><br /><sub><b>wangke6666</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wangke6666" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LWBobo"><img src="https://avatars.githubusercontent.com/u/50368698?v=4?s=100" width="100px;" alt="刺猬"/><br /><sub><b>刺猬</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/issues?q=author%3ALWBobo" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=LWBobo" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.zanglikun.com"><img src="https://avatars.githubusercontent.com/u/61591648?v=4?s=100" width="100px;" alt="Haste"/><br /><sub><b>Haste</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zanglikun" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SuitSmile"><img src="https://avatars.githubusercontent.com/u/38679717?v=4?s=100" width="100px;" alt="zhongshi.yi"/><br /><sub><b>zhongshi.yi</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=SuitSmile" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.smallq.cn"><img src="https://avatars.githubusercontent.com/u/39754275?v=4?s=100" width="100px;" alt="Qi Zhang"/><br /><sub><b>Qi Zhang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zzzhangqi" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MrAndyMing"><img src="https://avatars.githubusercontent.com/u/49541483?v=4?s=100" width="100px;" alt="MrAndyMing"/><br /><sub><b>MrAndyMing</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MrAndyMing" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://idongliming.github.io/"><img src="https://avatars.githubusercontent.com/u/31564353?v=4?s=100" width="100px;" alt="idongliming"/><br /><sub><b>idongliming</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=idongliming" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://earthjasonlin.github.io"><img src="https://avatars.githubusercontent.com/u/83632110?v=4?s=100" width="100px;" alt="Zichao Lin"/><br /><sub><b>Zichao Lin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=earthjasonlin" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=earthjasonlin" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://blog.liudonghua.com"><img src="https://avatars.githubusercontent.com/u/2276718?v=4?s=100" width="100px;" alt="liudonghua"/><br /><sub><b>liudonghua</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=liudonghua123" title="Code">💻</a> <a href="#ideas-liudonghua123" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/orangeyts"><img src="https://avatars.githubusercontent.com/u/4250869?v=4?s=100" width="100px;" alt="Jerry"/><br /><sub><b>Jerry</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=orangeyts" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=orangeyts" title="Tests">⚠️</a> <a href="#ideas-orangeyts" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://dynamictp.cn"><img src="https://avatars.githubusercontent.com/u/13051908?v=4?s=100" width="100px;" alt="yanhom"/><br /><sub><b>yanhom</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yanhom1314" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.jianshu.com/u/a8f822c04f67"><img src="https://avatars.githubusercontent.com/u/18587688?v=4?s=100" width="100px;" alt="fsl"/><br /><sub><b>fsl</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=fengshunli" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xttttv"><img src="https://avatars.githubusercontent.com/u/116323904?v=4?s=100" width="100px;" alt="xttttv"/><br /><sub><b>xttttv</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xttttv" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/NavinKumarBarnwal"><img src="https://avatars.githubusercontent.com/u/44504274?v=4?s=100" width="100px;" alt="NavinKumarBarnwal"/><br /><sub><b>NavinKumarBarnwal</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=NavinKumarBarnwal" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/z641205699"><img src="https://avatars.githubusercontent.com/u/45276423?v=4?s=100" width="100px;" alt="Zakkary"/><br /><sub><b>Zakkary</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=z641205699" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/898349230"><img src="https://avatars.githubusercontent.com/u/21972532?v=4?s=100" width="100px;" alt="sunxinbo"/><br /><sub><b>sunxinbo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=898349230" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=898349230" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ldzbook"><img src="https://avatars.githubusercontent.com/u/13903790?v=4?s=100" width="100px;" alt="ldzbook"/><br /><sub><b>ldzbook</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ldzbook" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Aldzbook" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SurryChen"><img src="https://avatars.githubusercontent.com/u/91116490?v=4?s=100" width="100px;" alt="余与雨"/><br /><sub><b>余与雨</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=SurryChen" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=SurryChen" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MysticalDream"><img src="https://avatars.githubusercontent.com/u/78899028?v=4?s=100" width="100px;" alt="MysticalDream"/><br /><sub><b>MysticalDream</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MysticalDream" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=MysticalDream" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zhouyoulin12"><img src="https://avatars.githubusercontent.com/u/17086633?v=4?s=100" width="100px;" alt="zhouyoulin12"/><br /><sub><b>zhouyoulin12</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zhouyoulin12" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=zhouyoulin12" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jerjjj"><img src="https://avatars.githubusercontent.com/u/93431283?v=4?s=100" width="100px;" alt="jerjjj"/><br /><sub><b>jerjjj</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jerjjj" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://wjl110.xyz/"><img src="https://avatars.githubusercontent.com/u/53851034?v=4?s=100" width="100px;" alt="wjl110"/><br /><sub><b>wjl110</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wjl110" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ngyhd"><img src="https://avatars.githubusercontent.com/u/29095207?v=4?s=100" width="100px;" alt="Sean"/><br /><sub><b>Sean</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ngyhd" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Daydreamer-ia"><img src="https://avatars.githubusercontent.com/u/83362909?v=4?s=100" width="100px;" alt="chenyiqin"/><br /><sub><b>chenyiqin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Daydreamer-ia" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Daydreamer-ia" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hudongdong129"><img src="https://avatars.githubusercontent.com/u/34374227?v=4?s=100" width="100px;" alt="hudongdong129"/><br /><sub><b>hudongdong129</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/commits?author=hudongdong129" title="Documentation">📖</a> <a href="#design-hudongdong129" title="Design">🎨</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TherChenYang"><img src="https://avatars.githubusercontent.com/u/124348939?v=4?s=100" width="100px;" alt="TherChenYang"/><br /><sub><b>TherChenYang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=TherChenYang" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=TherChenYang" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/HattoriHenzo"><img src="https://avatars.githubusercontent.com/u/5141285?v=4?s=100" width="100px;" alt="HattoriHenzo"/><br /><sub><b>HattoriHenzo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=HattoriHenzo" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=HattoriHenzo" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ycilry"><img src="https://avatars.githubusercontent.com/u/63967101?v=4?s=100" width="100px;" alt="ycilry"/><br /><sub><b>ycilry</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ycilry" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aoshiguchen"><img src="https://avatars.githubusercontent.com/u/10580997?v=4?s=100" width="100px;" alt="aoshiguchen"/><br /><sub><b>aoshiguchen</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=aoshiguchen" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=aoshiguchen" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/caibenxiang"><img src="https://avatars.githubusercontent.com/u/4568241?v=4?s=100" width="100px;" alt="蔡本祥"/><br /><sub><b>蔡本祥</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=caibenxiang" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.fckeverything.cn:4000/"><img src="https://avatars.githubusercontent.com/u/13827124?v=4?s=100" width="100px;" alt="浮游"/><br /><sub><b>浮游</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lifefloating" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Grass-Life"><img src="https://avatars.githubusercontent.com/u/114381513?v=4?s=100" width="100px;" alt="Grass-Life"/><br /><sub><b>Grass-Life</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Grass-Life" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xiaohe428"><img src="https://avatars.githubusercontent.com/u/99130317?v=4?s=100" width="100px;" alt="xiaohe428"/><br /><sub><b>xiaohe428</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xiaohe428" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=xiaohe428" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/baiban114"><img src="https://avatars.githubusercontent.com/u/59152619?v=4?s=100" width="100px;" alt="TableRow"/><br /><sub><b>TableRow</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=baiban114" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=baiban114" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ByteIDance"><img src="https://avatars.githubusercontent.com/u/100207562?v=4?s=100" width="100px;" alt="ByteIDance"/><br /><sub><b>ByteIDance</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ByteIDance" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mangel2002"><img src="https://avatars.githubusercontent.com/u/9348020?v=4?s=100" width="100px;" alt="Jangfe"/><br /><sub><b>Jangfe</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mangel2002" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zqr10159"><img src="https://avatars.githubusercontent.com/u/30048352?v=4?s=100" width="100px;" alt="zqr10159"/><br /><sub><b>zqr10159</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zqr10159" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=zqr10159" title="Code">💻</a> <a href="#blog-zqr10159" title="Blogposts">📝</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Azqr10159" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=zqr10159" title="Tests">⚠️</a> <a href="#design-zqr10159" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/vinci-897"><img src="https://avatars.githubusercontent.com/u/55838224?v=4?s=100" width="100px;" alt="vinci"/><br /><sub><b>vinci</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=vinci-897" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=vinci-897" title="Documentation">📖</a> <a href="#design-vinci-897" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/js110"><img src="https://avatars.githubusercontent.com/u/51191863?v=4?s=100" width="100px;" alt="js110"/><br /><sub><b>js110</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=js110" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JavaLionLi"><img src="https://avatars.githubusercontent.com/u/31852897?v=4?s=100" width="100px;" alt="CrazyLionLi"/><br /><sub><b>CrazyLionLi</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=JavaLionLi" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.banmajio.com"><img src="https://avatars.githubusercontent.com/u/53471385?v=4?s=100" width="100px;" alt="banmajio"/><br /><sub><b>banmajio</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=banmajio" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://suder.fun"><img src="https://avatars.githubusercontent.com/u/69955165?v=4?s=100" width="100px;" alt="topsuder"/><br /><sub><b>topsuder</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=topsuder" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/richar2022"><img src="https://avatars.githubusercontent.com/u/129016397?v=4?s=100" width="100px;" alt="richar2022"/><br /><sub><b>richar2022</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=richar2022" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fcb-xiaobo"><img src="https://avatars.githubusercontent.com/u/60566194?v=4?s=100" width="100px;" alt="fcb-xiaobo"/><br /><sub><b>fcb-xiaobo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=fcb-xiaobo" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wenkyzhang"><img src="https://avatars.githubusercontent.com/u/13983669?v=4?s=100" width="100px;" alt="wenkyzhang"/><br /><sub><b>wenkyzhang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wenkyzhang" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ZangJuxy"><img src="https://avatars.githubusercontent.com/u/71380295?v=4?s=100" width="100px;" alt="ZangJuxy"/><br /><sub><b>ZangJuxy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ZangJuxy" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/l646505418"><img src="https://avatars.githubusercontent.com/u/50475131?v=4?s=100" width="100px;" alt="l646505418"/><br /><sub><b>l646505418</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=l646505418" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Al646505418" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.carpewang.com"><img src="https://avatars.githubusercontent.com/u/78642589?v=4?s=100" width="100px;" alt="Carpe-Wang"/><br /><sub><b>Carpe-Wang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Carpe-Wang" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ACarpe-Wang" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/moshu023"><img src="https://avatars.githubusercontent.com/u/48593205?v=4?s=100" width="100px;" alt="莫枢"/><br /><sub><b>莫枢</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=moshu023" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/huangcanda"><img src="https://avatars.githubusercontent.com/u/4470566?v=4?s=100" width="100px;" alt="huangcanda"/><br /><sub><b>huangcanda</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=huangcanda" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.zrkizzy.com"><img src="https://avatars.githubusercontent.com/u/85340613?v=4?s=100" width="100px;" alt="世纪末的架构师"/><br /><sub><b>世纪末的架构师</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Architect-Java" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ShuningWan"><img src="https://avatars.githubusercontent.com/u/31086770?v=4?s=100" width="100px;" alt="ShuningWan"/><br /><sub><b>ShuningWan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ShuningWan" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MrYZhou"><img src="https://avatars.githubusercontent.com/u/44339602?v=4?s=100" width="100px;" alt="MrYZhou"/><br /><sub><b>MrYZhou</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MrYZhou" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/suncqujsj"><img src="https://avatars.githubusercontent.com/u/8012932?v=4?s=100" width="100px;" alt="suncqujsj"/><br /><sub><b>suncqujsj</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=suncqujsj" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sunqinbo"><img src="https://avatars.githubusercontent.com/u/1428540?v=4?s=100" width="100px;" alt="sunqinbo"/><br /><sub><b>sunqinbo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=sunqinbo" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/haoww"><img src="https://avatars.githubusercontent.com/u/32739294?v=4?s=100" width="100px;" alt="haoww"/><br /><sub><b>haoww</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=haoww" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/i-mayuan"><img src="https://avatars.githubusercontent.com/u/101498477?v=4?s=100" width="100px;" alt="i-mayuan"/><br /><sub><b>i-mayuan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=i-mayuan" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fengruge"><img src="https://avatars.githubusercontent.com/u/85803831?v=4?s=100" width="100px;" alt="fengruge"/><br /><sub><b>fengruge</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=fengruge" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aystzh"><img src="https://avatars.githubusercontent.com/u/38125392?v=4?s=100" width="100px;" alt="zhanghuan"/><br /><sub><b>zhanghuan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=aystzh" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shenyumin"><img src="https://avatars.githubusercontent.com/u/8438506?v=4?s=100" width="100px;" alt="shenymin"/><br /><sub><b>shenymin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=shenyumin" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dhruva1995"><img src="https://avatars.githubusercontent.com/u/12976351?v=4?s=100" width="100px;" alt="Dhruva Chandra"/><br /><sub><b>Dhruva Chandra</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=dhruva1995" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/weiwang988"><img src="https://avatars.githubusercontent.com/u/58241726?v=4?s=100" width="100px;" alt="miss_z"/><br /><sub><b>miss_z</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=weiwang988" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wyt990"><img src="https://avatars.githubusercontent.com/u/86013697?v=4?s=100" width="100px;" alt="wyt990"/><br /><sub><b>wyt990</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wyt990" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/licocon"><img src="https://avatars.githubusercontent.com/u/36863277?v=4?s=100" width="100px;" alt="licocon"/><br /><sub><b>licocon</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=licocon" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/2406450951"><img src="https://avatars.githubusercontent.com/u/48074721?v=4?s=100" width="100px;" alt="Mi Na"/><br /><sub><b>Mi Na</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=2406450951" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Kylin-Guo"><img src="https://avatars.githubusercontent.com/u/131239856?v=4?s=100" width="100px;" alt="Kylin-Guo"/><br /><sub><b>Kylin-Guo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Kylin-Guo" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/1797899698"><img src="https://avatars.githubusercontent.com/u/40411650?v=4?s=100" width="100px;" alt="Mr灬Dong先生"/><br /><sub><b>Mr灬Dong先生</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=1797899698" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://neilblaze.live"><img src="https://avatars.githubusercontent.com/u/48355572?v=4?s=100" width="100px;" alt="Pratyay Banerjee"/><br /><sub><b>Pratyay Banerjee</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Neilblaze" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=Neilblaze" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yujianzhong520"><img src="https://avatars.githubusercontent.com/u/63705063?v=4?s=100" width="100px;" alt="yujianzhong520"/><br /><sub><b>yujianzhong520</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yujianzhong520" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://sppan24.github.io/"><img src="https://avatars.githubusercontent.com/u/15795173?v=4?s=100" width="100px;" alt="SPPan"/><br /><sub><b>SPPan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=sppan24" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/1130600015"><img src="https://avatars.githubusercontent.com/u/67859663?v=4?s=100" width="100px;" alt="ZhangJiashu"/><br /><sub><b>ZhangJiashu</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=1130600015" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/QZmp236478"><img src="https://avatars.githubusercontent.com/u/56623162?v=4?s=100" width="100px;" alt="impress"/><br /><sub><b>impress</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=QZmp236478" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jx3775250"><img src="https://avatars.githubusercontent.com/u/40455946?v=4?s=100" width="100px;" alt="凌晨一点半"/><br /><sub><b>凌晨一点半</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jx3775250" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/eeshaanSA"><img src="https://avatars.githubusercontent.com/u/100678386?v=4?s=100" width="100px;" alt="Eeshaan Sawant"/><br /><sub><b>Eeshaan Sawant</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=eeshaanSA" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nandofromthebando"><img src="https://avatars.githubusercontent.com/u/87321214?v=4?s=100" width="100px;" alt="nandofromthebando"/><br /><sub><b>nandofromthebando</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=nandofromthebando" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/caiboking"><img src="https://avatars.githubusercontent.com/u/6509883?v=4?s=100" width="100px;" alt="caiboking"/><br /><sub><b>caiboking</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=caiboking" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/baixing99"><img src="https://avatars.githubusercontent.com/u/73473087?v=4?s=100" width="100px;" alt="baixing99"/><br /><sub><b>baixing99</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=baixing99" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ifrenzyc"><img src="https://avatars.githubusercontent.com/u/543927?v=4?s=100" width="100px;" alt="Yang Chuang"/><br /><sub><b>Yang Chuang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ifrenzyc" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wlin20"><img src="https://avatars.githubusercontent.com/u/20657577?v=4?s=100" width="100px;" alt="wlin20"/><br /><sub><b>wlin20</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wlin20" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/guojing1983"><img src="https://avatars.githubusercontent.com/u/60596094?v=4?s=100" width="100px;" alt="guojing1983"/><br /><sub><b>guojing1983</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=guojing1983" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/itxxq"><img src="https://avatars.githubusercontent.com/u/46962357?v=4?s=100" width="100px;" alt="moxi"/><br /><sub><b>moxi</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=itxxq" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/qq471754603"><img src="https://avatars.githubusercontent.com/u/23146592?v=4?s=100" width="100px;" alt="qq471754603"/><br /><sub><b>qq471754603</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=qq471754603" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/san346596324"><img src="https://avatars.githubusercontent.com/u/30828520?v=4?s=100" width="100px;" alt="渭雨"/><br /><sub><b>渭雨</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=san346596324" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/luoxuanzao"><img src="https://avatars.githubusercontent.com/u/44692579?v=4?s=100" width="100px;" alt="liuxuezhuo"/><br /><sub><b>liuxuezhuo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=luoxuanzao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lisongning"><img src="https://avatars.githubusercontent.com/u/93140178?v=4?s=100" width="100px;" alt="lisongning"/><br /><sub><b>lisongning</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lisongning" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/YutingNie"><img src="https://avatars.githubusercontent.com/u/104416402?v=4?s=100" width="100px;" alt="YutingNie"/><br /><sub><b>YutingNie</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=YutingNie" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=YutingNie" title="Documentation">📖</a> <a href="#design-YutingNie" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mikezzb"><img src="https://avatars.githubusercontent.com/u/23418428?v=4?s=100" width="100px;" alt="Mike Zhou"/><br /><sub><b>Mike Zhou</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mikezzb" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=mikezzb" title="Documentation">📖</a> <a href="#design-mikezzb" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lynx009"><img src="https://avatars.githubusercontent.com/u/105542329?v=4?s=100" width="100px;" alt="lynx009"/><br /><sub><b>lynx009</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lynx009" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/littlezhongzer"><img src="https://avatars.githubusercontent.com/u/33685289?v=4?s=100" width="100px;" alt="littlezhongzer"/><br /><sub><b>littlezhongzer</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=littlezhongzer" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ChenXiangxxxxx"><img src="https://avatars.githubusercontent.com/u/90089594?v=4?s=100" width="100px;" alt="ChenXiangxxxxx"/><br /><sub><b>ChenXiangxxxxx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ChenXiangxxxxx" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Mr-zhou315"><img src="https://avatars.githubusercontent.com/u/10276100?v=4?s=100" width="100px;" alt="Mr.zhou"/><br /><sub><b>Mr.zhou</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Mr-zhou315" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/XimfengYao"><img src="https://avatars.githubusercontent.com/u/17541537?v=4?s=100" width="100px;" alt="姚贤丰"/><br /><sub><b>姚贤丰</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=XimfengYao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LINGLUOJUN"><img src="https://avatars.githubusercontent.com/u/16778977?v=4?s=100" width="100px;" alt="lingluojun"/><br /><sub><b>lingluojun</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LINGLUOJUN" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.luelueking.com"><img src="https://avatars.githubusercontent.com/u/93204032?v=4?s=100" width="100px;" alt="1ue"/><br /><sub><b>1ue</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=luelueking" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.jimmyqiao.top"><img src="https://avatars.githubusercontent.com/u/67301054?v=4?s=100" width="100px;" alt="qyaaaa"/><br /><sub><b>qyaaaa</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=qyaaaa" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Aqyaaaa" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://novohit.top"><img src="https://avatars.githubusercontent.com/u/101090395?v=4?s=100" width="100px;" alt="novohit"/><br /><sub><b>novohit</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=novohit" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rbsrcy"><img src="https://avatars.githubusercontent.com/u/4798540?v=4?s=100" width="100px;" alt="zhuoshangyi"/><br /><sub><b>zhuoshangyi</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=rbsrcy" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ruanliang-hualun"><img src="https://avatars.githubusercontent.com/u/65543716?v=4?s=100" width="100px;" alt="ruanliang"/><br /><sub><b>ruanliang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ruanliang-hualun" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=ruanliang-hualun" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Eden4701"><img src="https://avatars.githubusercontent.com/u/68422437?v=4?s=100" width="100px;" alt="Eden4701"/><br /><sub><b>Eden4701</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Eden4701" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Eden4701" title="Documentation">📖</a> <a href="#design-Eden4701" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/XiaTian688"><img src="https://avatars.githubusercontent.com/u/111830921?v=4?s=100" width="100px;" alt="XiaTian688"/><br /><sub><b>XiaTian688</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=XiaTian688" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/liyin"><img src="https://avatars.githubusercontent.com/u/863169?v=4?s=100" width="100px;" alt="liyinjiang"/><br /><sub><b>liyinjiang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=liyin" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jiashu1024"><img src="https://avatars.githubusercontent.com/u/67859663?v=4?s=100" width="100px;" alt="ZhangJiashu"/><br /><sub><b>ZhangJiashu</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jiashu1024" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/1036664317"><img src="https://avatars.githubusercontent.com/u/7696697?v=4?s=100" width="100px;" alt="moghn"/><br /><sub><b>moghn</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=1036664317" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xiaoguolong"><img src="https://avatars.githubusercontent.com/u/33684988?v=4?s=100" width="100px;" alt="xiaoguolong"/><br /><sub><b>xiaoguolong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xiaoguolong" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Clownsw"><img src="https://avatars.githubusercontent.com/u/28394742?v=4?s=100" width="100px;" alt="Smliexx"/><br /><sub><b>Smliexx</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Clownsw" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AClownsw" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Calvin979"><img src="https://avatars.githubusercontent.com/u/131688897?v=4?s=100" width="100px;" alt="Calvin"/><br /><sub><b>Calvin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Calvin979" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=Calvin979" title="Code">💻</a> <a href="#design-Calvin979" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ACalvin979" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=Calvin979" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bbelide2"><img src="https://avatars.githubusercontent.com/u/26840796?v=4?s=100" width="100px;" alt="Bala Sukesh"/><br /><sub><b>Bala Sukesh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=bbelide2" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jinyaoMa"><img src="https://avatars.githubusercontent.com/u/25066570?v=4?s=100" width="100px;" alt="Jinyao Ma"/><br /><sub><b>Jinyao Ma</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jinyaoMa" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://linuxsuren.github.io/open-source-best-practice/"><img src="https://avatars.githubusercontent.com/u/1450685?v=4?s=100" width="100px;" alt="Rick"/><br /><sub><b>Rick</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LinuxSuRen" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=LinuxSuRen" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ZY945"><img src="https://avatars.githubusercontent.com/u/74083801?v=4?s=100" width="100px;" alt="东风"/><br /><sub><b>东风</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ZY945" title="Code">💻</a> <a href="#design-ZY945" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/commits?author=ZY945" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AZY945" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/prolevel1"><img src="https://avatars.githubusercontent.com/u/51995525?v=4?s=100" width="100px;" alt="sonam singh"/><br /><sub><b>sonam singh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=prolevel1" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ZhangZixuan1994"><img src="https://avatars.githubusercontent.com/u/20011653?v=4?s=100" width="100px;" alt="ZhangZixuan1994"/><br /><sub><b>ZhangZixuan1994</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ZhangZixuan1994" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hurenjie1"><img src="https://avatars.githubusercontent.com/u/40120355?v=4?s=100" width="100px;" alt="SHIG"/><br /><sub><b>SHIG</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=hurenjie1" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://tslj1024.github.io/"><img src="https://avatars.githubusercontent.com/u/155222677?v=4?s=100" width="100px;" alt="泰上老菌"/><br /><sub><b>泰上老菌</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=tslj1024" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ldysdu"><img src="https://avatars.githubusercontent.com/u/15815338?v=4?s=100" width="100px;" alt="ldysdu"/><br /><sub><b>ldysdu</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ldysdu" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GEM0816g"><img src="https://avatars.githubusercontent.com/u/85116017?v=4?s=100" width="100px;" alt="梁同学"/><br /><sub><b>梁同学</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=GEM0816g" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/avvCode"><img src="https://avatars.githubusercontent.com/u/113538532?v=4?s=100" width="100px;" alt="avv"/><br /><sub><b>avv</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=avvCode" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yqxxgh"><img src="https://avatars.githubusercontent.com/u/42080876?v=4?s=100" width="100px;" alt="yqxxgh"/><br /><sub><b>yqxxgh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yqxxgh" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=yqxxgh" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Ayqxxgh" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/CharlieShi46"><img src="https://avatars.githubusercontent.com/u/149798885?v=4?s=100" width="100px;" alt="CharlieShi46"/><br /><sub><b>CharlieShi46</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=CharlieShi46" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Nctllnty"><img src="https://avatars.githubusercontent.com/u/33241818?v=4?s=100" width="100px;" alt="Nctllnty"/><br /><sub><b>Nctllnty</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Nctllnty" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Wang-Yonghao"><img src="https://avatars.githubusercontent.com/u/48146606?v=4?s=100" width="100px;" alt="Wang-Yonghao"/><br /><sub><b>Wang-Yonghao</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Wang-Yonghao" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.yuque.com/dudiao/yy"><img src="https://avatars.githubusercontent.com/u/38355949?v=4?s=100" width="100px;" alt="读钓"/><br /><sub><b>读钓</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=dudiao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/starmilkxin"><img src="https://avatars.githubusercontent.com/u/55646681?v=4?s=100" width="100px;" alt="Xin"/><br /><sub><b>Xin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=starmilkxin" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Astarmilkxin" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/handy-git"><img src="https://avatars.githubusercontent.com/u/32837980?v=4?s=100" width="100px;" alt="handy"/><br /><sub><b>handy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=handy-git" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LiuTianyou"><img src="https://avatars.githubusercontent.com/u/30208283?v=4?s=100" width="100px;" alt="LiuTianyou"/><br /><sub><b>LiuTianyou</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LiuTianyou" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=LiuTianyou" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ALiuTianyou" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=LiuTianyou" title="Tests">⚠️</a> <a href="#blog-LiuTianyou" title="Blogposts">📝</a> <a href="#design-LiuTianyou" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/WinterKi1ler"><img src="https://avatars.githubusercontent.com/u/160592092?v=4?s=100" width="100px;" alt="WinterKi1ler"/><br /><sub><b>WinterKi1ler</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=WinterKi1ler" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://sharehoo.cn/"><img src="https://avatars.githubusercontent.com/u/45377370?v=4?s=100" width="100px;" alt="miki"/><br /><sub><b>miki</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=miki-hmt" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://codeflex.substack.com/"><img src="https://avatars.githubusercontent.com/u/85513042?v=4?s=100" width="100px;" alt="Keshav Carpenter"/><br /><sub><b>Keshav Carpenter</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=alpha951" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=alpha951" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/makechoicenow"><img src="https://avatars.githubusercontent.com/u/9911918?v=4?s=100" width="100px;" alt="makechoicenow"/><br /><sub><b>makechoicenow</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=makechoicenow" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gjjjj0101"><img src="https://avatars.githubusercontent.com/u/71874373?v=4?s=100" width="100px;" alt="Gao Jian"/><br /><sub><b>Gao Jian</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=gjjjj0101" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/commits?author=gjjjj0101" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=gjjjj0101" title="Documentation">📖</a> <a href="#design-gjjjj0101" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Agjjjj0101" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://jangto.tistory.com/"><img src="https://avatars.githubusercontent.com/u/37864182?v=4?s=100" width="100px;" alt="Hyeon Sung"/><br /><sub><b>Hyeon Sung</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=dukbong" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=dukbong" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://crossoverjie.top/"><img src="https://avatars.githubusercontent.com/u/15684156?v=4?s=100" width="100px;" alt="crossoverJie"/><br /><sub><b>crossoverJie</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=crossoverJie" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=crossoverJie" title="Documentation">📖</a> <a href="#blog-crossoverJie" title="Blogposts">📝</a> <a href="https://github.com/apache/hertzbeat/commits?author=crossoverJie" title="Tests">⚠️</a> <a href="#design-crossoverJie" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PeixyJ"><img src="https://avatars.githubusercontent.com/u/45998593?v=4?s=100" width="100px;" alt="PeixyJ"/><br /><sub><b>PeixyJ</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=PeixyJ" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Hi-Mr-Wind"><img src="https://avatars.githubusercontent.com/u/85803831?v=4?s=100" width="100px;" alt="风如歌"/><br /><sub><b>风如歌</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Hi-Mr-Wind" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MananPoojara"><img src="https://avatars.githubusercontent.com/u/104253184?v=4?s=100" width="100px;" alt="Manan Pujara"/><br /><sub><b>Manan Pujara</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MananPoojara" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xuziyang"><img src="https://avatars.githubusercontent.com/u/8465969?v=4?s=100" width="100px;" alt="xuziyang"/><br /><sub><b>xuziyang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xuziyang" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=xuziyang" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Axuziyang" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lwqzz"><img src="https://avatars.githubusercontent.com/u/62584513?v=4?s=100" width="100px;" alt="lwqzz"/><br /><sub><b>lwqzz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lwqzz" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/YxYL6125"><img src="https://avatars.githubusercontent.com/u/91076160?v=4?s=100" width="100px;" alt="YxYL"/><br /><sub><b>YxYL</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=YxYL6125" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tomorrowshipyltm"><img src="https://avatars.githubusercontent.com/u/61336903?v=4?s=100" width="100px;" alt="tomorrowshipyltm"/><br /><sub><b>tomorrowshipyltm</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=tomorrowshipyltm" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/15613060203"><img src="https://avatars.githubusercontent.com/u/41351615?v=4?s=100" width="100px;" alt="栗磊"/><br /><sub><b>栗磊</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=15613060203" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Alanxtl"><img src="https://avatars.githubusercontent.com/u/25652981?v=4?s=100" width="100px;" alt="Alan"/><br /><sub><b>Alan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Alanxtl" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.hadoop.wiki/"><img src="https://avatars.githubusercontent.com/u/29418975?v=4?s=100" width="100px;" alt="Jast"/><br /><sub><b>Jast</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zhangshenghang" title="Code">💻</a> <a href="#ideas-zhangshenghang" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/apache/hertzbeat/commits?author=zhangshenghang" title="Documentation">📖</a> <a href="#blog-zhangshenghang" title="Blogposts">📝</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Azhangshenghang" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=zhangshenghang" title="Tests">⚠️</a> <a href="#design-zhangshenghang" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zuobiao-zhou"><img src="https://avatars.githubusercontent.com/u/61108539?v=4?s=100" width="100px;" alt="Zhang Yuxuan"/><br /><sub><b>Zhang Yuxuan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=zuobiao-zhou" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=zuobiao-zhou" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Azuobiao-zhou" title="Bug reports">🐛</a> <a href="#blog-zuobiao-zhou" title="Blogposts">📝</a> <a href="https://github.com/apache/hertzbeat/commits?author=zuobiao-zhou" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Pzz-2021"><img src="https://avatars.githubusercontent.com/u/118056735?v=4?s=100" width="100px;" alt="P.P."/><br /><sub><b>P.P.</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Pzz-2021" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LLP2333"><img src="https://avatars.githubusercontent.com/u/61670545?v=4?s=100" width="100px;" alt="llp2333"/><br /><sub><b>llp2333</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LLP2333" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/HeartLinked"><img src="https://avatars.githubusercontent.com/u/78212101?v=4?s=100" width="100px;" alt="feiyang li"/><br /><sub><b>feiyang li</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=HeartLinked" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Aias00"><img src="https://avatars.githubusercontent.com/u/25810623?v=4?s=100" width="100px;" alt="aias00"/><br /><sub><b>aias00</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Aias00" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Aias00" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AAias00" title="Bug reports">🐛</a> <a href="#ideas-Aias00" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/apache/hertzbeat/commits?author=Aias00" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/westboy"><img src="https://avatars.githubusercontent.com/u/6385565?v=4?s=100" width="100px;" alt="Jin"/><br /><sub><b>Jin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=westboy" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.csdn.net/qq_52397471"><img src="https://avatars.githubusercontent.com/u/77964041?v=4?s=100" width="100px;" alt="YuLuo"/><br /><sub><b>YuLuo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yuluo-yx" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Ayuluo-yx" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=yuluo-yx" title="Tests">⚠️</a> <a href="#blog-yuluo-yx" title="Blogposts">📝</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Yanshuming1"><img src="https://avatars.githubusercontent.com/u/118667222?v=4?s=100" width="100px;" alt="linDong"/><br /><sub><b>linDong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Yanshuming1" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Yanshuming1" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AYanshuming1" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lwjxy"><img src="https://avatars.githubusercontent.com/u/52726400?v=4?s=100" width="100px;" alt="lwjxy"/><br /><sub><b>lwjxy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lwjxy" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://thespica.github.io/"><img src="https://avatars.githubusercontent.com/u/119573640?v=4?s=100" width="100px;" alt="John"/><br /><sub><b>John</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Thespica" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Thespica" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/boatrainlsz"><img src="https://avatars.githubusercontent.com/u/18243785?v=4?s=100" width="100px;" alt="boatrainlsz"/><br /><sub><b>boatrainlsz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=boatrainlsz" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.yitianyigexiangfa.com/"><img src="https://avatars.githubusercontent.com/u/3973419?v=4?s=100" width="100px;" alt="Bill Lau"/><br /><sub><b>Bill Lau</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=JavaProgrammerLB" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lw-yang"><img src="https://avatars.githubusercontent.com/u/23456873?v=4?s=100" width="100px;" alt="lwyang"/><br /><sub><b>lwyang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lw-yang" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xfl12345"><img src="https://avatars.githubusercontent.com/u/17960863?v=4?s=100" width="100px;" alt="xfl12345"/><br /><sub><b>xfl12345</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xfl12345" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yykaue"><img src="https://avatars.githubusercontent.com/u/22905143?v=4?s=100" width="100px;" alt="Limbo"/><br /><sub><b>Limbo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yykaue" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/irenhongyan"><img src="https://avatars.githubusercontent.com/u/53438321?v=4?s=100" width="100px;" alt="哈哈哈哈哈哈哈哈哈"/><br /><sub><b>哈哈哈哈哈哈哈哈哈</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=irenhongyan" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ileonli"><img src="https://avatars.githubusercontent.com/u/45332412?v=4?s=100" width="100px;" alt="Leon Li"/><br /><sub><b>Leon Li</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ileonli" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://fnil.net/"><img src="https://avatars.githubusercontent.com/u/14142?v=4?s=100" width="100px;" alt="dennis zhuang"/><br /><sub><b>dennis zhuang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=killme2008" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kerwin612"><img src="https://avatars.githubusercontent.com/u/3371163?v=4?s=100" width="100px;" alt="Kerwin Bryant"/><br /><sub><b>Kerwin Bryant</b></sub></a><br /><a href="#design-kerwin612" title="Design">🎨</a> <a href="https://github.com/apache/hertzbeat/commits?author=kerwin612" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=kerwin612" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Akerwin612" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ShineDevelopment"><img src="https://avatars.githubusercontent.com/u/59306780?v=4?s=100" width="100px;" alt="daixianglong"/><br /><sub><b>daixianglong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ShineDevelopment" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mchgood"><img src="https://avatars.githubusercontent.com/u/38482005?v=4?s=100" width="100px;" alt="mchgood"/><br /><sub><b>mchgood</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mchgood" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pwallk"><img src="https://avatars.githubusercontent.com/u/69385076?v=4?s=100" width="100px;" alt="kangli"/><br /><sub><b>kangli</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pwallk" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=pwallk" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Apwallk" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cdphantom"><img src="https://avatars.githubusercontent.com/u/12674795?v=4?s=100" width="100px;" alt="cdphantom"/><br /><sub><b>cdphantom</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=cdphantom" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/asd108908382"><img src="https://avatars.githubusercontent.com/u/77717999?v=4?s=100" width="100px;" alt="jiawei.guo"/><br /><sub><b>jiawei.guo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=asd108908382" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/QBH-insist"><img src="https://avatars.githubusercontent.com/u/39401478?v=4?s=100" width="100px;" alt="QBH-insist"/><br /><sub><b>QBH-insist</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=QBH-insist" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jiangsh-ui"><img src="https://avatars.githubusercontent.com/u/86990361?v=4?s=100" width="100px;" alt="jiangsh"/><br /><sub><b>jiangsh</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jiangsh-ui" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/keaifafafa"><img src="https://avatars.githubusercontent.com/u/83876361?v=4?s=100" width="100px;" alt="Keaifa"/><br /><sub><b>Keaifa</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=keaifafafa" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Akeaifafafa" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/loong95"><img src="https://avatars.githubusercontent.com/u/16333958?v=4?s=100" width="100px;" alt="Loong"/><br /><sub><b>Loong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=loong95" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ceekay47"><img src="https://avatars.githubusercontent.com/u/104664857?v=4?s=100" width="100px;" alt="Chandrakant Vankayalapati"/><br /><sub><b>Chandrakant Vankayalapati</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ceekay47" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MRgenial"><img src="https://avatars.githubusercontent.com/u/49973336?v=4?s=100" width="100px;" alt="b_mountain"/><br /><sub><b>b_mountain</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MRgenial" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TemirlanBasitov"><img src="https://avatars.githubusercontent.com/u/57500808?v=4?s=100" width="100px;" alt="TemirlanBasitov"/><br /><sub><b>TemirlanBasitov</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=TemirlanBasitov" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wyfvsfy"><img src="https://avatars.githubusercontent.com/u/11973517?v=4?s=100" width="100px;" alt="wyfvsfy"/><br /><sub><b>wyfvsfy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wyfvsfy" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sherry-peng2333"><img src="https://avatars.githubusercontent.com/u/70619577?v=4?s=100" width="100px;" alt="sherry-peng2333"/><br /><sub><b>sherry-peng2333</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=sherry-peng2333" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lixiaobaivv"><img src="https://avatars.githubusercontent.com/u/39290771?v=4?s=100" width="100px;" alt="Yzzz"/><br /><sub><b>Yzzz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lixiaobaivv" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.bckf.cn/"><img src="https://avatars.githubusercontent.com/u/13309008?v=4?s=100" width="100px;" alt="puruidong"/><br /><sub><b>puruidong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pruidong" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shinestare"><img src="https://avatars.githubusercontent.com/u/13570619?v=4?s=100" width="100px;" alt="shinestare"/><br /><sub><b>shinestare</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=shinestare" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/po-168"><img src="https://avatars.githubusercontent.com/u/185745593?v=4?s=100" width="100px;" alt="po-168"/><br /><sub><b>po-168</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=po-168" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/All-The-Best-for"><img src="https://avatars.githubusercontent.com/u/76414672?v=4?s=100" width="100px;" alt="wbs99"/><br /><sub><b>wbs99</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=All-The-Best-for" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/starryCoder"><img src="https://avatars.githubusercontent.com/u/46510059?v=4?s=100" width="100px;" alt="starryCoder"/><br /><sub><b>starryCoder</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=starryCoder" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hasimmollah"><img src="https://avatars.githubusercontent.com/u/32538599?v=4?s=100" width="100px;" alt="hasimmollah"/><br /><sub><b>hasimmollah</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=hasimmollah" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ayu-v0"><img src="https://avatars.githubusercontent.com/u/127600988?v=4?s=100" width="100px;" alt="Ayu"/><br /><sub><b>Ayu</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ayu-v0" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Rancho-7"><img src="https://avatars.githubusercontent.com/u/59016860?v=4?s=100" width="100px;" alt="Nick Guo"/><br /><sub><b>Nick Guo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Rancho-7" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=Rancho-7" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ARancho-7" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/doveLin0818"><img src="https://avatars.githubusercontent.com/u/190927907?v=4?s=100" width="100px;" alt="doveLin"/><br /><sub><b>doveLin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=doveLin0818" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://zzrl.cc/"><img src="https://avatars.githubusercontent.com/u/91836599?v=4?s=100" width="100px;" alt="yunfan24"/><br /><sub><b>yunfan24</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yunfan24" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=yunfan24" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Ayunfan24" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=yunfan24" title="Tests">⚠️</a> <a href="#blog-yunfan24" title="Blogposts">📝</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lctking"><img src="https://avatars.githubusercontent.com/u/168249998?v=4?s=100" width="100px;" alt="nullwli"/><br /><sub><b>nullwli</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lctking" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://simonsigre.com/"><img src="https://avatars.githubusercontent.com/u/14932913?v=4?s=100" width="100px;" alt="Simon Sigré"/><br /><sub><b>Simon Sigré</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=simonsigre" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=simonsigre" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://www.ponfee.cn/"><img src="https://avatars.githubusercontent.com/u/46117331?v=4?s=100" width="100px;" alt="ponfee"/><br /><sub><b>ponfee</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ponfee" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Vedant7789"><img src="https://avatars.githubusercontent.com/u/147625492?v=4?s=100" width="100px;" alt="Vedant7789"/><br /><sub><b>Vedant7789</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Vedant7789" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Craaaaazy77"><img src="https://avatars.githubusercontent.com/u/23025522?v=4?s=100" width="100px;" alt="Craaaaazy77"/><br /><sub><b>Craaaaazy77</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Craaaaazy77" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Suvrat1629"><img src="https://avatars.githubusercontent.com/u/140749446?v=4?s=100" width="100px;" alt="Suvrat1629"/><br /><sub><b>Suvrat1629</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Suvrat1629" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://ghyghoo8.github.io/"><img src="https://avatars.githubusercontent.com/u/363129?v=4?s=100" width="100px;" alt="ghy"/><br /><sub><b>ghy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ghyghoo8" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/helei1030"><img src="https://avatars.githubusercontent.com/u/11839080?v=4?s=100" width="100px;" alt="helei1030"/><br /><sub><b>helei1030</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=helei1030" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://medium.com/@pjfanning"><img src="https://avatars.githubusercontent.com/u/11783444?v=4?s=100" width="100px;" alt="PJ Fanning"/><br /><sub><b>PJ Fanning</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pjfanning" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Apjfanning" title="Bug reports">🐛</a> <a href="https://github.com/apache/hertzbeat/commits?author=pjfanning" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MonsterChenzhuo"><img src="https://avatars.githubusercontent.com/u/60029759?v=4?s=100" width="100px;" alt="monster"/><br /><sub><b>monster</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MonsterChenzhuo" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MasamiYui"><img src="https://avatars.githubusercontent.com/u/22274133?v=4?s=100" width="100px;" alt="Sherlock Yin"/><br /><sub><b>Sherlock Yin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=MasamiYui" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=MasamiYui" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3AMasamiYui" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wanhao23"><img src="https://avatars.githubusercontent.com/u/29560961?v=4?s=100" width="100px;" alt="wanhao"/><br /><sub><b>wanhao</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=wanhao23" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=wanhao23" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jonasHanhan"><img src="https://avatars.githubusercontent.com/u/130035609?v=4?s=100" width="100px;" alt="jonasHanhan"/><br /><sub><b>jonasHanhan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jonasHanhan" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/NikhilMurugesan"><img src="https://avatars.githubusercontent.com/u/49281792?v=4?s=100" width="100px;" alt="NikhilMurugesan"/><br /><sub><b>NikhilMurugesan</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=NikhilMurugesan" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/myangle1120"><img src="https://avatars.githubusercontent.com/u/19237013?v=4?s=100" width="100px;" alt="myangle1120"/><br /><sub><b>myangle1120</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=myangle1120" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yasminvo"><img src="https://avatars.githubusercontent.com/u/107528848?v=4?s=100" width="100px;" alt="yasminvo"/><br /><sub><b>yasminvo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yasminvo" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/notbugggg"><img src="https://avatars.githubusercontent.com/u/147966331?v=4?s=100" width="100px;" alt="不关银渐层的事哦"/><br /><sub><b>不关银渐层的事哦</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=notbugggg" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Anotbugggg" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yyahang"><img src="https://avatars.githubusercontent.com/u/90464876?v=4?s=100" width="100px;" alt="yyahang"/><br /><sub><b>yyahang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yyahang" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JuJinPark"><img src="https://avatars.githubusercontent.com/u/44892459?v=4?s=100" width="100px;" alt="jujin"/><br /><sub><b>jujin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=JuJinPark" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=JuJinPark" title="Documentation">📖</a> <a href="#ideas-JuJinPark" title="Ideas, Planning, & Feedback">🤔</a> <a href="#blog-JuJinPark" title="Blogposts">📝</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LL-LIN"><img src="https://avatars.githubusercontent.com/u/43002118?v=4?s=100" width="100px;" alt="LL-LIN"/><br /><sub><b>LL-LIN</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=LL-LIN" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3ALL-LIN" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://bigcyy.github.io/"><img src="https://avatars.githubusercontent.com/u/73413979?v=4?s=100" width="100px;" alt="Yang Chen"/><br /><sub><b>Yang Chen</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=bigcyy" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=bigcyy" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Abigcyy" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sarthakeash"><img src="https://avatars.githubusercontent.com/u/74091160?v=4?s=100" width="100px;" alt="Sarthak Arora"/><br /><sub><b>Sarthak Arora</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=sarthakeash" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=sarthakeash" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PengJingzhao"><img src="https://avatars.githubusercontent.com/u/97368949?v=4?s=100" width="100px;" alt="彭镜肇"/><br /><sub><b>彭镜肇</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=PengJingzhao" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gagaradio"><img src="https://avatars.githubusercontent.com/u/18532370?v=4?s=100" width="100px;" alt="Walter Jia"/><br /><sub><b>Walter Jia</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=gagaradio" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/boyucjz"><img src="https://avatars.githubusercontent.com/u/18730041?v=4?s=100" width="100px;" alt="boyucjz"/><br /><sub><b>boyucjz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=boyucjz" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Cyanty"><img src="https://avatars.githubusercontent.com/u/153884653?v=4?s=100" width="100px;" alt="Cyanty"/><br /><sub><b>Cyanty</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Cyanty" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=Cyanty" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KevinLLF"><img src="https://avatars.githubusercontent.com/u/85452733?v=4?s=100" width="100px;" alt="Jay丿167"/><br /><sub><b>Jay丿167</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=KevinLLF" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Duansg"><img src="https://avatars.githubusercontent.com/u/112607719?v=4?s=100" width="100px;" alt="Duansg"/><br /><sub><b>Duansg</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Duansg" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xiaomizhou2"><img src="https://avatars.githubusercontent.com/u/47807926?v=4?s=100" width="100px;" alt="zhangyaxi"/><br /><sub><b>zhangyaxi</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=xiaomizhou2" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=xiaomizhou2" title="Tests">⚠️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RainBondsongyg"><img src="https://avatars.githubusercontent.com/u/94501396?v=4?s=100" width="100px;" alt="songyg"/><br /><sub><b>songyg</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=RainBondsongyg" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lx1229"><img src="https://avatars.githubusercontent.com/u/44620005?v=4?s=100" width="100px;" alt="Liuxin"/><br /><sub><b>Liuxin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lx1229" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yy549159265"><img src="https://avatars.githubusercontent.com/u/40821310?v=4?s=100" width="100px;" alt="yy549159265"/><br /><sub><b>yy549159265</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yy549159265" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=yy549159265" title="Tests">⚠️</a> <a href="#design-yy549159265" title="Design">🎨</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cto-huhang"><img src="https://avatars.githubusercontent.com/u/53338629?v=4?s=100" width="100px;" alt="cto-huhang"/><br /><sub><b>cto-huhang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=cto-huhang" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Saramanda9988"><img src="https://avatars.githubusercontent.com/u/176664901?v=4?s=100" width="100px;" alt="LunaRain_079"/><br /><sub><b>LunaRain_079</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Saramanda9988" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/delei"><img src="https://avatars.githubusercontent.com/u/17263766?v=4?s=100" width="100px;" alt="DeleiGuo"/><br /><sub><b>DeleiGuo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=delei" title="Documentation">📖</a> <a href="https://github.com/apache/hertzbeat/commits?author=delei" title="Code">💻</a> <a href="https://github.com/apache/hertzbeat/commits?author=delei" title="Tests">⚠️</a> <a href="https://github.com/apache/hertzbeat/issues?q=author%3Adelei" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/chingjustwe"><img src="https://avatars.githubusercontent.com/u/13643747?v=4?s=100" width="100px;" alt="Rocky, Chi"/><br /><sub><b>Rocky, Chi</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=chingjustwe" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rowankid"><img src="https://avatars.githubusercontent.com/u/18652781?v=4?s=100" width="100px;" alt="Wenqi Luo"/><br /><sub><b>Wenqi Luo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/issues?q=author%3Arowankid" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tuzuy"><img src="https://avatars.githubusercontent.com/u/95274591?v=4?s=100" width="100px;" alt="tuzuy"/><br /><sub><b>tuzuy</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=tuzuy" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/carlpinto25"><img src="https://avatars.githubusercontent.com/u/117299909?v=4?s=100" width="100px;" alt="carl pinto"/><br /><sub><b>carl pinto</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=carlpinto25" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://cxhello.top/"><img src="https://avatars.githubusercontent.com/u/49056040?v=4?s=100" width="100px;" alt="cxhello"/><br /><sub><b>cxhello</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=cxhello" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jl15988"><img src="https://avatars.githubusercontent.com/u/70638770?v=4?s=100" width="100px;" alt="会功夫的李白"/><br /><sub><b>会功夫的李白</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=jl15988" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.aytop.cloud/"><img src="https://avatars.githubusercontent.com/u/37127008?v=4?s=100" width="100px;" alt="Albert.Yang"/><br /><sub><b>Albert.Yang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=AlbertYang0801" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.tokenlen.top/"><img src="https://avatars.githubusercontent.com/u/150590575?v=4?s=100" width="100px;" alt="zhou yong kang"/><br /><sub><b>zhou yong kang</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=mengnankkkk" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/warrobe"><img src="https://avatars.githubusercontent.com/u/89446159?v=4?s=100" width="100px;" alt="warrobe"/><br /><sub><b>warrobe</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=warrobe" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Jetiaime"><img src="https://avatars.githubusercontent.com/u/93769000?v=4?s=100" width="100px;" alt="TeAmo"/><br /><sub><b>TeAmo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Jetiaime" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pentium100"><img src="https://avatars.githubusercontent.com/u/27917?v=4?s=100" width="100px;" alt="pentium100"/><br /><sub><b>pentium100</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pentium100" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dedyks"><img src="https://avatars.githubusercontent.com/u/23741665?v=4?s=100" width="100px;" alt="Dedy Kurniawan Santoso"/><br /><sub><b>Dedy Kurniawan Santoso</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=dedyks" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-restore -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
## 💬 コミュニティ交流
|
||||
|
||||
##### チャネル
|
||||
|
||||
[メール](https://lists.apache.org/list.html?dev@hertzbeat.apache.org) : メーリングリストに登録するために、```dev-subscribe@hertzbeat.apache.org```にメールを送ってください。
|
||||
|
||||
[Chat On Discord](https://discord.gg/Fb6M73htGr)
|
||||
|
||||
WeChatグループ : `ahertzbeat` を検索.
|
||||
|
||||
WeChat公式アカウント : `usthecom`を検索.
|
||||
|
||||
[QQグループ](https://qm.qq.com/q/xxqecSC2cw) : グループ番号 `1035688434`
|
||||
|
||||
[Github Discussion](https://github.com/apache/hertzbeat/discussions)
|
||||
|
||||
[Reddit Community](https://www.reddit.com/r/hertzbeat/)
|
||||
|
||||
[Follow Us Twitter](https://x.com/hertzbeat1024)
|
||||
|
||||
[Subscribe YouTube](https://www.youtube.com/channel/UCri75zfWX0GHqJFPENEbLow)
|
||||
|
||||
|
||||
##### Open-Source Project Build From Open-Source
|
||||
|
||||
HertzBeat is built on so many great open source projects, thanks to them!
|
||||
|
||||
- `Java Spring SpringBoot Jpa Maven Assembly Netty Lombok Sureness Protobuf HttpClient Guava SnakeYaml JsonPath ...`
|
||||
- `TypeScript Angular NG-ZORRO NG-ALAIN NodeJs Npm Html Less Echarts Rxjs ZoneJs MonacoEditor SlickCarousel Docusaurus ...`
|
||||
|
||||
|
||||
## Landscape
|
||||
|
||||
<p align="left">
|
||||
<img src="./home/static/img/home/cncf-landscape-left-logo.svg" width="300"> <img src="./home/static/img/home/cncf-right-logo.svg" width="345" />
|
||||
<br /><br />
|
||||
HertzBeat has been included in the <a href="https://landscape.cncf.io/?item=observability-and-analysis--observability--hertzbeat">
|
||||
CNCF Observability And Analysis - Observability Landscape.</a>
|
||||
</p>
|
||||
|
||||
## 🛡️ License
|
||||
[`Apache License, Version 2.0`](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
+9
-6
@@ -1,12 +1,9 @@
|
||||
# Security Policy
|
||||
|
||||
**[Version Releases](https://github.com/apache/hertzbeat/releases)**
|
||||
Apache HertzBeat™ follows the [ASF vulnerability handling process](https://apache.org/security/#vulnerability-handling)
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Use this section to tell people about which versions of your project are
|
||||
currently being supported with security updates.
|
||||
|
||||
| Version | Supported |
|
||||
|-------------------|--------------------|
|
||||
| Latest Version | :white_check_mark: |
|
||||
@@ -14,6 +11,12 @@ currently being supported with security updates.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Use this section to tell people how to report a vulnerability.
|
||||
Please do not file GitHub issues for security vulnerabilities as they are public!
|
||||
|
||||
Please report vulnerability disclosures to `security@apache.org`.
|
||||
To report a new vulnerability you have discovered please follow the [ASF vulnerability reporting process](https://apache.org/security/#reporting-a-vulnerability).
|
||||
|
||||
## Extra Information
|
||||
|
||||
- [Apache HertzBeat™ Security Model](https://hertzbeat.apache.org/docs/help/security_model)
|
||||
- [ASF Security](https://www.apache.org/security/)
|
||||
- [Apache HertzBeat™ Document](https://hertzbeat.apache.org/docs/)
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hertzbeat</artifactId>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hertzbeat-alerter</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- common -->
|
||||
<dependency>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat-common</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- kafka -->
|
||||
<dependency>
|
||||
<groupId>org.apache.kafka</groupId>
|
||||
<artifactId>kafka-clients</artifactId>
|
||||
</dependency>
|
||||
<!--redis-->
|
||||
<dependency>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- swagger -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.afterturn</groupId>
|
||||
<artifactId>easypoi-annotation</artifactId>
|
||||
<version>4.3.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
<version>4.1.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* alarm module thread pool
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class AlerterWorkerPool {
|
||||
|
||||
private ThreadPoolExecutor workerExecutor;
|
||||
|
||||
public AlerterWorkerPool() {
|
||||
initWorkExecutor();
|
||||
}
|
||||
|
||||
private void initWorkExecutor() {
|
||||
ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setUncaughtExceptionHandler((thread, throwable) -> {
|
||||
log.error("workerExecutor has uncaughtException.");
|
||||
log.error(throwable.getMessage(), throwable);
|
||||
})
|
||||
.setDaemon(true)
|
||||
.setNameFormat("alerter-worker-%d")
|
||||
.build();
|
||||
workerExecutor = new ThreadPoolExecutor(6,
|
||||
10,
|
||||
10,
|
||||
TimeUnit.SECONDS,
|
||||
new SynchronousQueue<>(),
|
||||
threadFactory,
|
||||
new ThreadPoolExecutor.AbortPolicy());
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the alerter task
|
||||
* @param runnable task
|
||||
* @throws RejectedExecutionException when The thread pool is full of
|
||||
*/
|
||||
public void executeJob(Runnable runnable) throws RejectedExecutionException {
|
||||
workerExecutor.execute(runnable);
|
||||
}
|
||||
}
|
||||
@@ -1,482 +0,0 @@
|
||||
/*
|
||||
* 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.calculate;
|
||||
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.ALERT_STATUS_CODE_NOT_REACH;
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.ALERT_STATUS_CODE_PENDING;
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.ALERT_STATUS_CODE_SOLVED;
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.TAG_CODE;
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.TAG_METRIC;
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.TAG_METRICS;
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.TAG_MONITOR_APP;
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.TAG_MONITOR_ID;
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.TAG_MONITOR_NAME;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.jexl3.JexlException;
|
||||
import org.apache.commons.jexl3.JexlExpression;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.alert.AlerterWorkerPool;
|
||||
import org.apache.hertzbeat.alert.dao.AlertMonitorDao;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineService;
|
||||
import org.apache.hertzbeat.alert.service.AlertService;
|
||||
import org.apache.hertzbeat.alert.util.AlertTemplateUtil;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.entity.manager.TagItem;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.queue.CommonDataQueue;
|
||||
import org.apache.hertzbeat.common.support.event.MonitorDeletedEvent;
|
||||
import org.apache.hertzbeat.common.support.event.SystemConfigChangeEvent;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.apache.hertzbeat.common.util.JexlExpressionRunner;
|
||||
import org.apache.hertzbeat.common.util.ResourceBundleUtil;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Calculate alarms based on the alarm definition rules and collected data
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class CalculateAlarm {
|
||||
|
||||
private static final String SYSTEM_VALUE_ROW_COUNT = "system_value_row_count";
|
||||
|
||||
/**
|
||||
* The alarm in the process is triggered
|
||||
* key - monitorId+alertDefineId+tags | The alarm is a common threshold alarm
|
||||
* key - monitorId | Indicates the monitoring status availability reachability alarm
|
||||
*/
|
||||
private final Map<String, Alert> triggeredAlertMap;
|
||||
/**
|
||||
* The not recover alert
|
||||
* key - monitorId + alertDefineId + tags
|
||||
*/
|
||||
private final Map<String, Alert> notRecoveredAlertMap;
|
||||
private final AlerterWorkerPool workerPool;
|
||||
private final CommonDataQueue dataQueue;
|
||||
private final AlertDefineService alertDefineService;
|
||||
private final AlarmCommonReduce alarmCommonReduce;
|
||||
private ResourceBundle bundle;
|
||||
private final AlertService alertService;
|
||||
|
||||
public CalculateAlarm(AlerterWorkerPool workerPool, CommonDataQueue dataQueue,
|
||||
AlertDefineService alertDefineService, AlertMonitorDao monitorDao,
|
||||
AlarmCommonReduce alarmCommonReduce, AlertService alertService) {
|
||||
this.workerPool = workerPool;
|
||||
this.dataQueue = dataQueue;
|
||||
this.alarmCommonReduce = alarmCommonReduce;
|
||||
this.alertDefineService = alertDefineService;
|
||||
this.alertService = alertService;
|
||||
this.bundle = ResourceBundleUtil.getBundle("alerter");
|
||||
this.triggeredAlertMap = new ConcurrentHashMap<>(16);
|
||||
this.notRecoveredAlertMap = new ConcurrentHashMap<>(16);
|
||||
// Initialize stateAlertMap
|
||||
List<Monitor> monitors = monitorDao.findMonitorsByStatus(CommonConstants.MONITOR_DOWN_CODE);
|
||||
if (monitors != null) {
|
||||
for (Monitor monitor : monitors) {
|
||||
HashMap<String, String> tags = new HashMap<>(8);
|
||||
tags.put(TAG_MONITOR_ID, String.valueOf(monitor.getId()));
|
||||
tags.put(TAG_MONITOR_NAME, monitor.getName());
|
||||
tags.put(TAG_MONITOR_APP, monitor.getApp());
|
||||
this.notRecoveredAlertMap.put(monitor.getId() + CommonConstants.AVAILABILITY,
|
||||
Alert.builder().tags(tags).target(CommonConstants.AVAILABILITY).status(ALERT_STATUS_CODE_PENDING).build());
|
||||
}
|
||||
}
|
||||
startCalculate();
|
||||
}
|
||||
|
||||
private void startCalculate() {
|
||||
Runnable runnable = () -> {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
try {
|
||||
CollectRep.MetricsData metricsData = dataQueue.pollMetricsDataToAlerter();
|
||||
if (metricsData != null) {
|
||||
calculate(metricsData);
|
||||
}
|
||||
} catch (InterruptedException ignored) {
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("calculate alarm error: {}.", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
workerPool.executeJob(runnable);
|
||||
workerPool.executeJob(runnable);
|
||||
workerPool.executeJob(runnable);
|
||||
}
|
||||
|
||||
private void calculate(CollectRep.MetricsData metricsData) {
|
||||
long currentTimeMilli = System.currentTimeMillis();
|
||||
long monitorId = metricsData.getId();
|
||||
String app = metricsData.getApp();
|
||||
if (app.startsWith(CommonConstants.PROMETHEUS_APP_PREFIX)) {
|
||||
app = CommonConstants.PROMETHEUS;
|
||||
}
|
||||
String metrics = metricsData.getMetrics();
|
||||
// If the metrics whose scheduling priority is 0 has the status of collecting response data UN_REACHABLE/UN_CONNECTABLE,
|
||||
// the highest severity alarm is generated to monitor the status change
|
||||
if (metricsData.getPriority() == 0) {
|
||||
handlerAvailableMetrics(monitorId, app, metricsData);
|
||||
}
|
||||
// Query the alarm definitions associated with the metrics of the monitoring type
|
||||
// field - define[]
|
||||
Map<String, List<AlertDefine>> defineMap = alertDefineService.getMonitorBindAlertDefines(monitorId, app, metrics);
|
||||
if (defineMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<CollectRep.Field> fields = metricsData.getFieldsList();
|
||||
Map<String, Object> fieldValueMap = new HashMap<>(8);
|
||||
int valueRowCount = metricsData.getValuesCount();
|
||||
for (Map.Entry<String, List<AlertDefine>> entry : defineMap.entrySet()) {
|
||||
List<AlertDefine> defines = entry.getValue();
|
||||
for (AlertDefine define : defines) {
|
||||
final String expr = define.getExpr();
|
||||
if (StringUtils.isBlank(expr)) {
|
||||
continue;
|
||||
}
|
||||
if (expr.contains(SYSTEM_VALUE_ROW_COUNT) && metricsData.getValuesCount() == 0) {
|
||||
fieldValueMap.put(SYSTEM_VALUE_ROW_COUNT, valueRowCount);
|
||||
try {
|
||||
boolean match = execAlertExpression(fieldValueMap, expr);
|
||||
try {
|
||||
if (match) {
|
||||
// If the threshold rule matches, the number of times the threshold has been triggered is determined and an alarm is triggered
|
||||
afterThresholdRuleMatch(currentTimeMilli, monitorId, app, metrics, "", fieldValueMap, define);
|
||||
// if the threshold is triggered, ignore other data rows
|
||||
continue;
|
||||
} else {
|
||||
String alarmKey = String.valueOf(monitorId) + define.getId();
|
||||
triggeredAlertMap.remove(alarmKey);
|
||||
if (define.isRecoverNotice()) {
|
||||
handleRecoveredAlert(currentTimeMilli, define, expr, alarmKey);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
for (CollectRep.ValueRow valueRow : metricsData.getValuesList()) {
|
||||
|
||||
if (CollectionUtils.isEmpty(valueRow.getColumnsList())) {
|
||||
continue;
|
||||
}
|
||||
fieldValueMap.clear();
|
||||
fieldValueMap.put(SYSTEM_VALUE_ROW_COUNT, valueRowCount);
|
||||
StringBuilder tagBuilder = new StringBuilder();
|
||||
for (int index = 0; index < valueRow.getColumnsList().size(); index++) {
|
||||
String valueStr = valueRow.getColumns(index);
|
||||
if (CommonConstants.NULL_VALUE.equals(valueStr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final CollectRep.Field field = fields.get(index);
|
||||
final int fieldType = field.getType();
|
||||
|
||||
if (fieldType == CommonConstants.TYPE_NUMBER) {
|
||||
final Double doubleValue;
|
||||
if ((doubleValue = CommonUtil.parseStrDouble(valueStr)) != null) {
|
||||
fieldValueMap.put(field.getName(), doubleValue);
|
||||
}
|
||||
} else if (fieldType == CommonConstants.TYPE_TIME) {
|
||||
final Integer integerValue;
|
||||
if ((integerValue = CommonUtil.parseStrInteger(valueStr)) != null) {
|
||||
fieldValueMap.put(field.getName(), integerValue);
|
||||
}
|
||||
} else {
|
||||
if (StringUtils.isNotEmpty(valueStr)) {
|
||||
fieldValueMap.put(field.getName(), valueStr);
|
||||
}
|
||||
}
|
||||
|
||||
if (field.getLabel()) {
|
||||
tagBuilder.append("-").append(valueStr);
|
||||
}
|
||||
}
|
||||
try {
|
||||
boolean match = execAlertExpression(fieldValueMap, expr);
|
||||
try {
|
||||
if (match) {
|
||||
// If the threshold rule matches, the number of times the threshold has been triggered is determined and an alarm is triggered
|
||||
afterThresholdRuleMatch(currentTimeMilli, monitorId, app, metrics, tagBuilder.toString(), fieldValueMap, define);
|
||||
} else {
|
||||
String alarmKey = String.valueOf(monitorId) + define.getId() + tagBuilder;
|
||||
triggeredAlertMap.remove(alarmKey);
|
||||
if (define.isRecoverNotice()) {
|
||||
handleRecoveredAlert(currentTimeMilli, define, expr, alarmKey);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRecoveredAlert(long currentTimeMilli, AlertDefine define, String expr, String alarmKey) {
|
||||
Alert notResolvedAlert = notRecoveredAlertMap.remove(alarmKey);
|
||||
if (notResolvedAlert != null) {
|
||||
// Sending an alarm Restore
|
||||
Map<String, String> tags = notResolvedAlert.getTags();
|
||||
String content = this.bundle.getString("alerter.alarm.recover") + " : " + expr;
|
||||
Alert resumeAlert = Alert.builder()
|
||||
.tags(tags)
|
||||
.target(define.getApp() + "." + define.getMetric() + "." + define.getField())
|
||||
.content(content)
|
||||
.priority(CommonConstants.ALERT_PRIORITY_CODE_WARNING)
|
||||
.status(CommonConstants.ALERT_STATUS_CODE_RESTORED)
|
||||
.firstAlarmTime(currentTimeMilli)
|
||||
.lastAlarmTime(notResolvedAlert.getLastAlarmTime())
|
||||
.triggerTimes(1)
|
||||
.build();
|
||||
alarmCommonReduce.reduceAndSendAlarm(resumeAlert);
|
||||
}
|
||||
}
|
||||
|
||||
private void afterThresholdRuleMatch(long currentTimeMilli, long monitorId, String app, String metrics, String tagStr,
|
||||
Map<String, Object> fieldValueMap, AlertDefine define) {
|
||||
String alarmKey = String.valueOf(monitorId) + define.getId() + tagStr;
|
||||
Alert triggeredAlert = triggeredAlertMap.get(alarmKey);
|
||||
if (triggeredAlert != null) {
|
||||
int times = triggeredAlert.getTriggerTimes() + 1;
|
||||
triggeredAlert.setTriggerTimes(times);
|
||||
triggeredAlert.setFirstAlarmTime(currentTimeMilli);
|
||||
triggeredAlert.setLastAlarmTime(currentTimeMilli);
|
||||
int defineTimes = define.getTimes() == null ? 1 : define.getTimes();
|
||||
if (times >= defineTimes) {
|
||||
triggeredAlert.setStatus(ALERT_STATUS_CODE_PENDING);
|
||||
triggeredAlertMap.remove(alarmKey);
|
||||
notRecoveredAlertMap.put(alarmKey, triggeredAlert);
|
||||
alarmCommonReduce.reduceAndSendAlarm(triggeredAlert.clone());
|
||||
}
|
||||
} else {
|
||||
fieldValueMap.put(TAG_MONITOR_APP, app);
|
||||
fieldValueMap.put(TAG_METRICS, metrics);
|
||||
fieldValueMap.put(TAG_METRIC, define.getField());
|
||||
Map<String, String> tags = new HashMap<>(8);
|
||||
tags.put(TAG_MONITOR_ID, String.valueOf(monitorId));
|
||||
tags.put(TAG_MONITOR_APP, app);
|
||||
tags.put(CommonConstants.TAG_THRESHOLD_ID, String.valueOf(define.getId()));
|
||||
if (!CollectionUtils.isEmpty(define.getTags())) {
|
||||
for (TagItem tagItem : define.getTags()) {
|
||||
fieldValueMap.put(tagItem.getName(), tagItem.getValue());
|
||||
tags.put(tagItem.getName(), tagItem.getValue());
|
||||
}
|
||||
}
|
||||
Alert alert = Alert.builder()
|
||||
.tags(tags)
|
||||
.priority(define.getPriority())
|
||||
.status(ALERT_STATUS_CODE_NOT_REACH)
|
||||
.target(app + "." + metrics + "." + define.getField())
|
||||
.triggerTimes(1)
|
||||
.firstAlarmTime(currentTimeMilli)
|
||||
.lastAlarmTime(currentTimeMilli)
|
||||
// Keyword matching and substitution in the template
|
||||
.content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap))
|
||||
.build();
|
||||
int defineTimes = define.getTimes() == null ? 1 : define.getTimes();
|
||||
if (1 >= defineTimes) {
|
||||
alert.setStatus(ALERT_STATUS_CODE_PENDING);
|
||||
notRecoveredAlertMap.put(alarmKey, alert);
|
||||
alarmCommonReduce.reduceAndSendAlarm(alert.clone());
|
||||
} else {
|
||||
triggeredAlertMap.put(alarmKey, alert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean execAlertExpression(Map<String, Object> fieldValueMap, String expr) {
|
||||
Boolean match;
|
||||
JexlExpression expression;
|
||||
try {
|
||||
expression = JexlExpressionRunner.compile(expr);
|
||||
} catch (JexlException jexlException) {
|
||||
log.error("Alarm Rule: {} Compile Error: {}.", expr, jexlException.getMessage());
|
||||
throw jexlException;
|
||||
} catch (Exception e) {
|
||||
log.error("Alarm Rule: {} Unknown Error: {}.", expr, e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
|
||||
try {
|
||||
match = (Boolean) JexlExpressionRunner.evaluate(expression, fieldValueMap);
|
||||
} catch (JexlException jexlException) {
|
||||
log.error("Alarm Rule: {} Run Error: {}.", expr, jexlException.getMessage());
|
||||
throw jexlException;
|
||||
} catch (Exception e) {
|
||||
log.error("Alarm Rule: {} Unknown Error: {}.", expr, e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
return match != null && match;
|
||||
}
|
||||
|
||||
private void handlerAvailableMetrics(long monitorId, String app, CollectRep.MetricsData metricsData) {
|
||||
if (metricsData.getCode() == CollectRep.Code.TIMEOUT) {
|
||||
return;
|
||||
}
|
||||
// TODO CACHE getMonitorBindAlertAvaDefine
|
||||
AlertDefine avaAlertDefine = alertDefineService.getMonitorBindAlertAvaDefine(monitorId, app, CommonConstants.AVAILABILITY);
|
||||
if (avaAlertDefine == null) {
|
||||
return;
|
||||
}
|
||||
long currentTimeMill = System.currentTimeMillis();
|
||||
if (metricsData.getCode() != CollectRep.Code.SUCCESS) {
|
||||
Alert preAlert = triggeredAlertMap.get(String.valueOf(monitorId));
|
||||
Map<String, String> tags = new HashMap<>(6);
|
||||
tags.put(TAG_MONITOR_ID, String.valueOf(monitorId));
|
||||
tags.put(TAG_MONITOR_APP, app);
|
||||
tags.put(CommonConstants.TAG_THRESHOLD_ID, String.valueOf(avaAlertDefine.getId()));
|
||||
tags.put(TAG_METRICS, CommonConstants.AVAILABILITY);
|
||||
tags.put(TAG_CODE, metricsData.getCode().name());
|
||||
Map<String, Object> valueMap = tags.entrySet().stream()
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
if (!CollectionUtils.isEmpty(avaAlertDefine.getTags())) {
|
||||
for (TagItem tagItem : avaAlertDefine.getTags()) {
|
||||
valueMap.put(tagItem.getName(), tagItem.getValue());
|
||||
tags.put(tagItem.getName(), tagItem.getValue());
|
||||
}
|
||||
}
|
||||
if (preAlert == null) {
|
||||
Alert.AlertBuilder alertBuilder = Alert.builder()
|
||||
.tags(tags)
|
||||
.priority(avaAlertDefine.getPriority())
|
||||
.status(ALERT_STATUS_CODE_NOT_REACH)
|
||||
.target(CommonConstants.AVAILABILITY)
|
||||
.content(AlertTemplateUtil.render(avaAlertDefine.getTemplate(), valueMap))
|
||||
.firstAlarmTime(currentTimeMill)
|
||||
.lastAlarmTime(currentTimeMill)
|
||||
.triggerTimes(1);
|
||||
if (avaAlertDefine.getTimes() == null || avaAlertDefine.getTimes() <= 1) {
|
||||
String notResolvedAlertKey = monitorId + CommonConstants.AVAILABILITY;
|
||||
alertBuilder.status(ALERT_STATUS_CODE_PENDING);
|
||||
notRecoveredAlertMap.put(notResolvedAlertKey, alertBuilder.build());
|
||||
alarmCommonReduce.reduceAndSendAlarm(alertBuilder.build());
|
||||
} else {
|
||||
triggeredAlertMap.put(String.valueOf(monitorId), alertBuilder.build());
|
||||
}
|
||||
} else {
|
||||
int times = preAlert.getTriggerTimes() + 1;
|
||||
preAlert.setTriggerTimes(times);
|
||||
preAlert.setFirstAlarmTime(currentTimeMill);
|
||||
preAlert.setLastAlarmTime(currentTimeMill);
|
||||
int defineTimes = avaAlertDefine.getTimes() == null ? 1 : avaAlertDefine.getTimes();
|
||||
if (times >= defineTimes) {
|
||||
preAlert.setStatus(ALERT_STATUS_CODE_PENDING);
|
||||
String notResolvedAlertKey = monitorId + CommonConstants.AVAILABILITY;
|
||||
notRecoveredAlertMap.put(notResolvedAlertKey, preAlert.clone());
|
||||
alarmCommonReduce.reduceAndSendAlarm(preAlert.clone());
|
||||
triggeredAlertMap.remove(String.valueOf(monitorId));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check whether an availability or unreachable alarm is generated before the association monitoring
|
||||
// and send a clear alarm to clear the monitoring status
|
||||
triggeredAlertMap.remove(String.valueOf(monitorId));
|
||||
String notResolvedAlertKey = monitorId + CommonConstants.AVAILABILITY;
|
||||
Alert notResolvedAlert = notRecoveredAlertMap.remove(notResolvedAlertKey);
|
||||
if (notResolvedAlert != null) {
|
||||
// Sending an alarm Restore
|
||||
Map<String, String> tags = notResolvedAlert.getTags();
|
||||
if (!avaAlertDefine.isRecoverNotice()) {
|
||||
tags.put(CommonConstants.IGNORE, CommonConstants.IGNORE);
|
||||
}
|
||||
String content = this.bundle.getString("alerter.availability.recover");
|
||||
Alert resumeAlert = Alert.builder()
|
||||
.tags(tags)
|
||||
.target(CommonConstants.AVAILABILITY)
|
||||
.content(content)
|
||||
.priority(CommonConstants.ALERT_PRIORITY_CODE_WARNING)
|
||||
.status(CommonConstants.ALERT_STATUS_CODE_RESTORED)
|
||||
.firstAlarmTime(currentTimeMill)
|
||||
.lastAlarmTime(notResolvedAlert.getLastAlarmTime())
|
||||
.triggerTimes(1)
|
||||
.build();
|
||||
alarmCommonReduce.reduceAndSendAlarm(resumeAlert);
|
||||
Runnable updateStatusJob = () -> {
|
||||
// todo update pre all type alarm status
|
||||
updateAvailabilityAlertStatus(monitorId, resumeAlert);
|
||||
};
|
||||
workerPool.executeJob(updateStatusJob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAvailabilityAlertStatus(long monitorId, Alert restoreAlert) {
|
||||
List<Alert> availabilityAlerts = queryAvailabilityAlerts(monitorId, restoreAlert);
|
||||
availabilityAlerts.stream().parallel().forEach(alert -> {
|
||||
log.info("updating alert status solved id: {}", alert.getId());
|
||||
alertService.editAlertStatus(ALERT_STATUS_CODE_SOLVED, List.of(alert.getId()));
|
||||
});
|
||||
}
|
||||
|
||||
private List<Alert> queryAvailabilityAlerts(long monitorId, Alert restoreAlert) {
|
||||
//create query condition
|
||||
Specification<Alert> specification = (root, query, criteriaBuilder) -> {
|
||||
List<Predicate> andList = new ArrayList<>();
|
||||
|
||||
Predicate predicateTags = criteriaBuilder.like(root.get("tags").as(String.class), "%" + monitorId + "%");
|
||||
andList.add(predicateTags);
|
||||
|
||||
Predicate predicatePriority = criteriaBuilder.equal(root.get("priority"), CommonConstants.ALERT_PRIORITY_CODE_EMERGENCY);
|
||||
andList.add(predicatePriority);
|
||||
|
||||
Predicate predicateStatus = criteriaBuilder.equal(root.get("status"), ALERT_STATUS_CODE_PENDING);
|
||||
andList.add(predicateStatus);
|
||||
|
||||
Predicate predicateAlertTime = criteriaBuilder.lessThanOrEqualTo(root.get("lastAlarmTime"), restoreAlert.getLastAlarmTime());
|
||||
andList.add(predicateAlertTime);
|
||||
|
||||
Predicate[] predicates = new Predicate[andList.size()];
|
||||
return criteriaBuilder.and(andList.toArray(predicates));
|
||||
};
|
||||
|
||||
//query results
|
||||
return alertService.getAlerts(specification);
|
||||
}
|
||||
|
||||
@EventListener(SystemConfigChangeEvent.class)
|
||||
public void onSystemConfigChangeEvent(SystemConfigChangeEvent event) {
|
||||
log.info("calculate alarm receive system config change event: {}.", event.getSource());
|
||||
this.bundle = ResourceBundleUtil.getBundle("alerter");
|
||||
}
|
||||
|
||||
@EventListener(MonitorDeletedEvent.class)
|
||||
public void onMonitorDeletedEvent(MonitorDeletedEvent event) {
|
||||
log.info("calculate alarm receive monitor {} has been deleted.", event.getMonitorId());
|
||||
this.triggeredAlertMap.remove(String.valueOf(event.getMonitorId()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/**
|
||||
* Alert auto configuration.
|
||||
*/
|
||||
@ComponentScan(basePackages = "org.apache.hertzbeat.alert")
|
||||
public class AlerterAutoConfiguration {
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.MONITOR_NOT_EXIST_CODE;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import org.apache.hertzbeat.alert.service.AlertConvergeService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertConverge;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Alarm Converge management API
|
||||
*/
|
||||
@Tag(name = "Alert Converge API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/converge", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertConvergeController {
|
||||
|
||||
@Autowired
|
||||
private AlertConvergeService alertConvergeService;
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "New Alarm Converge", description = "Added an alarm Converge")
|
||||
public ResponseEntity<Message<Void>> addNewAlertConverge(@Valid @RequestBody AlertConverge alertConverge) {
|
||||
alertConvergeService.validate(alertConverge, false);
|
||||
alertConvergeService.addAlertConverge(alertConverge);
|
||||
return ResponseEntity.ok(Message.success("Add success"));
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Operation(summary = "Modifying an Alarm Converge", description = "Modify an existing alarm Converge")
|
||||
public ResponseEntity<Message<Void>> modifyAlertConverge(@Valid @RequestBody AlertConverge alertConverge) {
|
||||
alertConvergeService.validate(alertConverge, true);
|
||||
alertConvergeService.modifyAlertConverge(alertConverge);
|
||||
return ResponseEntity.ok(Message.success("Modify success"));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{id}")
|
||||
@Operation(summary = "Querying Alarm Converge",
|
||||
description = "You can obtain alarm Converge information based on the alarm Converge ID")
|
||||
public ResponseEntity<Message<AlertConverge>> getAlertConverge(
|
||||
@Parameter(description = "Alarm Converge ID", example = "6565463543") @PathVariable("id") long id) {
|
||||
AlertConverge alertConverge = alertConvergeService.getAlertConverge(id);
|
||||
if (alertConverge == null) {
|
||||
return ResponseEntity.ok(Message.fail(MONITOR_NOT_EXIST_CODE, "AlertConverge not exist."));
|
||||
} else {
|
||||
return ResponseEntity.ok(Message.success(alertConverge));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-106
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.alert.service.AlertConvergeService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertConverge;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Converge the batch API for alarms
|
||||
*/
|
||||
@Tag(name = "Alert Converge Batch API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/converges", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertConvergesController {
|
||||
|
||||
@Autowired
|
||||
private AlertConvergeService alertConvergeService;
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "Query the alarm converge list",
|
||||
description = "You can obtain the list of alarm converge by querying filter items")
|
||||
public ResponseEntity<Message<Page<AlertConverge>>> getAlertConverges(
|
||||
@Parameter(description = "Alarm Converge ID", example = "6565463543") @RequestParam(required = false) List<Long> ids,
|
||||
@Parameter(description = "Search Name", example = "x") @RequestParam(required = false) String search,
|
||||
@Parameter(description = "Sort field, default id", example = "id") @RequestParam(defaultValue = "id") String sort,
|
||||
@Parameter(description = "Sort mode: asc: ascending, desc: descending", example = "desc") @RequestParam(defaultValue = "desc") String order,
|
||||
@Parameter(description = "List current page", example = "0") @RequestParam(defaultValue = "0") int pageIndex,
|
||||
@Parameter(description = "Number of list pages", example = "8") @RequestParam(defaultValue = "8") int pageSize) {
|
||||
|
||||
Specification<AlertConverge> specification = (root, query, criteriaBuilder) -> {
|
||||
List<Predicate> andList = new ArrayList<>();
|
||||
if (ids != null && !ids.isEmpty()) {
|
||||
CriteriaBuilder.In<Long> inPredicate = criteriaBuilder.in(root.get("id"));
|
||||
for (long id : ids) {
|
||||
inPredicate.value(id);
|
||||
}
|
||||
andList.add(inPredicate);
|
||||
}
|
||||
if (StringUtils.hasText(search)) {
|
||||
Predicate predicate = criteriaBuilder.or(
|
||||
criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("name")),
|
||||
"%" + search.toLowerCase() + "%"
|
||||
)
|
||||
);
|
||||
andList.add(predicate);
|
||||
}
|
||||
Predicate[] predicates = new Predicate[andList.size()];
|
||||
return criteriaBuilder.and(andList.toArray(predicates));
|
||||
};
|
||||
Sort sortExp = Sort.by(new Sort.Order(Sort.Direction.fromString(order), sort));
|
||||
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sortExp);
|
||||
Page<AlertConverge> alertConvergePage = alertConvergeService.getAlertConverges(specification, pageRequest);
|
||||
return ResponseEntity.ok(Message.success(alertConvergePage));
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@Operation(summary = "Delete alarm converge in batches",
|
||||
description = "Delete alarm converge in batches based on the alarm converge ID list")
|
||||
public ResponseEntity<Message<Void>> deleteAlertDefines(
|
||||
@Parameter(description = "Alarm Converge IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids
|
||||
) {
|
||||
if (ids != null && !ids.isEmpty()) {
|
||||
alertConvergeService.deleteAlertConverges(new HashSet<>(ids));
|
||||
}
|
||||
return ResponseEntity.ok(Message.success());
|
||||
}
|
||||
|
||||
}
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.MONITOR_NOT_EXIST_CODE;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefineMonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Alarm definition management API
|
||||
*/
|
||||
@Tag(name = "Alert Define API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/define", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertDefineController {
|
||||
|
||||
@Autowired
|
||||
private AlertDefineService alertDefineService;
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "New Alarm Definition", description = "Added an alarm definition")
|
||||
public ResponseEntity<Message<Void>> addNewAlertDefine(@Valid @RequestBody AlertDefine alertDefine) {
|
||||
// Verify request data
|
||||
alertDefineService.validate(alertDefine, false);
|
||||
alertDefineService.addAlertDefine(alertDefine);
|
||||
return ResponseEntity.ok(Message.success("Add success"));
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Operation(summary = "Modifying an Alarm Definition", description = "Modify an existing alarm definition")
|
||||
public ResponseEntity<Message<Void>> modifyAlertDefine(@Valid @RequestBody AlertDefine alertDefine) {
|
||||
// Verify request data
|
||||
alertDefineService.validate(alertDefine, true);
|
||||
alertDefineService.modifyAlertDefine(alertDefine);
|
||||
return ResponseEntity.ok(Message.success("Modify success"));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{id}")
|
||||
@Operation(summary = "Querying Alarm Definitions",
|
||||
description = "You can obtain alarm definition information based on the alarm definition ID")
|
||||
public ResponseEntity<Message<AlertDefine>> getAlertDefine(
|
||||
@Parameter(description = "Alarm Definition ID", example = "6565463543") @PathVariable("id") long id) {
|
||||
// Obtaining Monitoring Information
|
||||
AlertDefine alertDefine = alertDefineService.getAlertDefine(id);
|
||||
if (alertDefine == null) {
|
||||
return ResponseEntity.ok(Message.fail(MONITOR_NOT_EXIST_CODE, "AlertDefine not exist."));
|
||||
} else {
|
||||
return ResponseEntity.ok(Message.success(alertDefine));
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping(path = "/{id}")
|
||||
@Operation(summary = "Deleting an Alarm Definition",
|
||||
description = "If the alarm definition does not exist, the alarm is deleted successfully")
|
||||
public ResponseEntity<Message<Void>> deleteAlertDefine(
|
||||
@Parameter(description = "Alarm Definition ID", example = "6565463543") @PathVariable("id") long id) {
|
||||
// If the alarm definition does not exist or is deleted successfully, the deletion succeeds
|
||||
alertDefineService.deleteAlertDefine(id);
|
||||
return ResponseEntity.ok(Message.success("Delete success"));
|
||||
}
|
||||
|
||||
@PostMapping(path = "/{alertDefineId}/monitors")
|
||||
@Operation(summary = "Application alarm definition is associated with monitoring",
|
||||
description = "Applies the association between specified alarm definitions and monitoring")
|
||||
public ResponseEntity<Message<Void>> applyAlertDefineMonitorsBind(
|
||||
@Parameter(description = "Alarm Definition ID", example = "6565463543") @PathVariable("alertDefineId") long alertDefineId,
|
||||
@RequestBody List<AlertDefineMonitorBind> alertDefineMonitorBinds) {
|
||||
alertDefineService.applyBindAlertDefineMonitors(alertDefineId, alertDefineMonitorBinds);
|
||||
return ResponseEntity.ok(Message.success("Apply success"));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{alertDefineId}/monitors")
|
||||
@Operation(summary = "Application alarm definition is associated with monitoring",
|
||||
description = "Applies the association between specified alarm definitions and monitoring")
|
||||
public ResponseEntity<Message<List<AlertDefineMonitorBind>>> getAlertDefineMonitorsBind(
|
||||
@Parameter(description = "Alarm Definition ID", example = "6565463543") @PathVariable("alertDefineId") long alertDefineId) {
|
||||
List<AlertDefineMonitorBind> defineBinds = alertDefineService.getBindAlertDefineMonitors(alertDefineId);
|
||||
defineBinds = defineBinds.stream().filter(item -> item.getMonitor() != null).collect(Collectors.toList());
|
||||
return ResponseEntity.ok(Message.success(defineBinds));
|
||||
}
|
||||
|
||||
}
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* Define the batch API for alarms
|
||||
*/
|
||||
@Tag(name = "Alert Define Batch API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/defines", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertDefinesController {
|
||||
|
||||
@Autowired
|
||||
private AlertDefineService alertDefineService;
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "Example Query the alarm definition list",
|
||||
description = "You can obtain the list of alarm definitions by querying filter items")
|
||||
public ResponseEntity<Message<Page<AlertDefine>>> getAlertDefines(
|
||||
@Parameter(description = "Alarm Definition ID", example = "6565463543") @RequestParam(required = false) List<Long> ids,
|
||||
@Parameter(description = "Search-Target Expr Template", example = "x") @RequestParam(required = false) String search,
|
||||
@Parameter(description = "Alarm Definition Severity", example = "6565463543") @RequestParam(required = false) Byte priority,
|
||||
@Parameter(description = "Sort field, default id", example = "id") @RequestParam(defaultValue = "id") String sort,
|
||||
@Parameter(description = "Sort mode: asc: ascending, desc: descending", example = "desc") @RequestParam(defaultValue = "desc") String order,
|
||||
@Parameter(description = "List current page", example = "0") @RequestParam(defaultValue = "0") int pageIndex,
|
||||
@Parameter(description = "Number of list pages", example = "8") @RequestParam(defaultValue = "8") int pageSize) {
|
||||
|
||||
Specification<AlertDefine> specification = (root, query, criteriaBuilder) -> {
|
||||
List<Predicate> andList = new ArrayList<>();
|
||||
if (ids != null && !ids.isEmpty()) {
|
||||
CriteriaBuilder.In<Long> inPredicate = criteriaBuilder.in(root.get("id"));
|
||||
for (long id : ids) {
|
||||
inPredicate.value(id);
|
||||
}
|
||||
andList.add(inPredicate);
|
||||
}
|
||||
if (StringUtils.hasText(search)) {
|
||||
Predicate predicate = criteriaBuilder.or(
|
||||
criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("app")),
|
||||
"%" + search.toLowerCase() + "%"
|
||||
),
|
||||
criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("metric")),
|
||||
"%" + search.toLowerCase() + "%"
|
||||
),
|
||||
criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("field")),
|
||||
"%" + search.toLowerCase() + "%"
|
||||
),
|
||||
criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("expr")),
|
||||
"%" + search.toLowerCase() + "%"
|
||||
),
|
||||
criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("template")),
|
||||
"%" + search.toLowerCase() + "%"
|
||||
)
|
||||
);
|
||||
andList.add(predicate);
|
||||
}
|
||||
if (priority != null) {
|
||||
Predicate predicate = criteriaBuilder.equal(root.get("priority"), priority);
|
||||
andList.add(predicate);
|
||||
}
|
||||
Predicate[] predicates = new Predicate[andList.size()];
|
||||
return criteriaBuilder.and(andList.toArray(predicates));
|
||||
};
|
||||
Sort sortExp = Sort.by(new Sort.Order(Sort.Direction.fromString(order), sort));
|
||||
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sortExp);
|
||||
Page<AlertDefine> alertDefinePage = alertDefineService.getAlertDefines(specification, pageRequest);
|
||||
return ResponseEntity.ok(Message.success(alertDefinePage));
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@Operation(summary = "Delete alarm definitions in batches",
|
||||
description = "Delete alarm definitions in batches based on the alarm definition ID list")
|
||||
public ResponseEntity<Message<Void>> deleteAlertDefines(
|
||||
@Parameter(description = "Alarm Definition IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids
|
||||
) {
|
||||
if (ids != null && !ids.isEmpty()) {
|
||||
alertDefineService.deleteAlertDefines(new HashSet<>(ids));
|
||||
}
|
||||
return ResponseEntity.ok(Message.success());
|
||||
}
|
||||
|
||||
@GetMapping("/export")
|
||||
@Operation(summary = "export alertDefine config", description = "export alarm definition configuration")
|
||||
public void export(
|
||||
@Parameter(description = "AlertDefine ID List", example = "656937901") @RequestParam List<Long> ids,
|
||||
@Parameter(description = "Export Type:JSON,EXCEL,YAML") @RequestParam(defaultValue = "JSON") String type,
|
||||
HttpServletResponse res) throws Exception {
|
||||
alertDefineService.export(ids, type, res);
|
||||
}
|
||||
|
||||
@PostMapping("/import")
|
||||
@Operation(summary = "import alertDefine config", description = "import alarm definition configuration")
|
||||
public ResponseEntity<Message<Void>> importDefines(MultipartFile file) throws Exception {
|
||||
alertDefineService.importConfig(file);
|
||||
return ResponseEntity.ok(Message.success("Import success"));
|
||||
}
|
||||
}
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dto.CloudAlertReportAbstract;
|
||||
import org.apache.hertzbeat.alert.dto.GeneralCloudAlertReport;
|
||||
import org.apache.hertzbeat.alert.enums.CloudServiceAlarmInformationEnum;
|
||||
import org.apache.hertzbeat.alert.service.AlertService;
|
||||
import org.apache.hertzbeat.common.entity.dto.AlertReport;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Extern Alarm Manage API
|
||||
*/
|
||||
@Tag(name = "Extern Alarm Manage API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alerts/report", produces = {APPLICATION_JSON_VALUE})
|
||||
@Slf4j
|
||||
public class AlertReportController {
|
||||
|
||||
@Autowired
|
||||
private AlertService alertService;
|
||||
|
||||
@PostMapping("/{cloud}")
|
||||
@Operation(summary = "Interface for reporting external alarm information of cloud service")
|
||||
public ResponseEntity<Message<Void>> addNewAlertReportFromCloud(@PathVariable("cloud") String cloudServiceName,
|
||||
@RequestBody String alertReport) {
|
||||
CloudServiceAlarmInformationEnum cloudService = CloudServiceAlarmInformationEnum
|
||||
.getEnumFromCloudServiceName(cloudServiceName);
|
||||
|
||||
AlertReport alert = null;
|
||||
if (cloudService != null) {
|
||||
try {
|
||||
CloudAlertReportAbstract cloudAlertReport = JsonUtil
|
||||
.fromJson(alertReport, cloudService.getCloudServiceAlarmInformationEntity());
|
||||
assert cloudAlertReport != null;
|
||||
alert = AlertReport.builder()
|
||||
.content(cloudAlertReport.getContent())
|
||||
.alertName(cloudAlertReport.getAlertName())
|
||||
.alertTime(cloudAlertReport.getAlertTime())
|
||||
.alertDuration(cloudAlertReport.getAlertDuration())
|
||||
.priority(cloudAlertReport.getPriority())
|
||||
.reportType(cloudAlertReport.getReportType())
|
||||
.labels(cloudAlertReport.getLabels())
|
||||
.annotations(cloudAlertReport.getAnnotations())
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("[alert report] parse cloud service alarm content failed! cloud service: {} conrent: {}",
|
||||
cloudService.name(), alertReport);
|
||||
}
|
||||
} else {
|
||||
alert = AlertReport.builder()
|
||||
.content("error do not has cloud service api")
|
||||
.alertName("/api/alerts/report/" + cloudServiceName)
|
||||
.alertTime(new Date().getTime())
|
||||
.priority(1)
|
||||
.reportType(1)
|
||||
.build();
|
||||
}
|
||||
Optional.ofNullable(alert).ifPresent(alertReportPresent ->
|
||||
alertService.addNewAlertReport(alertReportPresent));
|
||||
return ResponseEntity.ok(Message.success("Add report success"));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "Interface for reporting external and general alarm information",
|
||||
description = "The interface is used to report external and general alarm information")
|
||||
public ResponseEntity<Message<Void>> addNewAlertReport(@RequestBody GeneralCloudAlertReport alertReport) {
|
||||
alertReport.refreshAlertTime();
|
||||
alertService.addNewAlertReport(alertReport);
|
||||
return ResponseEntity.ok(Message.success("Add report success"));
|
||||
}
|
||||
}
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.alert.service.AlertSilenceService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertSilence;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Silence the batch API for alarms
|
||||
*/
|
||||
@Tag(name = "Alert Silence Batch API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/silences", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertSilencesController {
|
||||
|
||||
@Autowired
|
||||
private AlertSilenceService alertSilenceService;
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "Query the alarm silence list",
|
||||
description = "You can obtain the list of alarm silence by querying filter items")
|
||||
public ResponseEntity<Message<Page<AlertSilence>>> getAlertSilences(
|
||||
@Parameter(description = "Alarm Silence ID", example = "6565463543") @RequestParam(required = false) List<Long> ids,
|
||||
@Parameter(description = "Search Name", example = "x") @RequestParam(required = false) String search,
|
||||
@Parameter(description = "Sort field, default id", example = "id") @RequestParam(defaultValue = "id") String sort,
|
||||
@Parameter(description = "Sort mode: asc: ascending, desc: descending", example = "desc") @RequestParam(defaultValue = "desc") String order,
|
||||
@Parameter(description = "List current page", example = "0") @RequestParam(defaultValue = "0") int pageIndex,
|
||||
@Parameter(description = "Number of list pages", example = "8") @RequestParam(defaultValue = "8") int pageSize) {
|
||||
|
||||
Specification<AlertSilence> specification = (root, query, criteriaBuilder) -> {
|
||||
List<Predicate> andList = new ArrayList<>();
|
||||
if (ids != null && !ids.isEmpty()) {
|
||||
CriteriaBuilder.In<Long> inPredicate = criteriaBuilder.in(root.get("id"));
|
||||
for (long id : ids) {
|
||||
inPredicate.value(id);
|
||||
}
|
||||
andList.add(inPredicate);
|
||||
}
|
||||
if (StringUtils.hasText(search)) {
|
||||
Predicate predicate = criteriaBuilder.or(
|
||||
criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("name")),
|
||||
"%" + search.toLowerCase() + "%"
|
||||
)
|
||||
);
|
||||
andList.add(predicate);
|
||||
}
|
||||
Predicate[] predicates = new Predicate[andList.size()];
|
||||
return criteriaBuilder.and(andList.toArray(predicates));
|
||||
};
|
||||
Sort sortExp = Sort.by(new Sort.Order(Sort.Direction.fromString(order), sort));
|
||||
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sortExp);
|
||||
Page<AlertSilence> alertSilencePage = alertSilenceService.getAlertSilences(specification, pageRequest);
|
||||
Message<Page<AlertSilence>> message = Message.success(alertSilencePage);
|
||||
return ResponseEntity.ok(message);
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@Operation(summary = "Delete alarm silence in batches",
|
||||
description = "Delete alarm silence in batches based on the alarm silence ID list")
|
||||
public ResponseEntity<Message<Void>> deleteAlertDefines(
|
||||
@Parameter(description = "Alarm Silence IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids
|
||||
) {
|
||||
if (ids != null && !ids.isEmpty()) {
|
||||
alertSilenceService.deleteAlertSilences(new HashSet<>(ids));
|
||||
}
|
||||
Message<Void> message = Message.success();
|
||||
return ResponseEntity.ok(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.alert.dto.AlertSummary;
|
||||
import org.apache.hertzbeat.alert.service.AlertService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Alarm Management API
|
||||
*/
|
||||
@Tag(name = "Alarm Manage Batch API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alerts", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertsController {
|
||||
|
||||
@Autowired
|
||||
private AlertService alertService;
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "Get a list of alarm information based on query filter items", description = "according to the query filter items to obtain a list of alarm information")
|
||||
public ResponseEntity<Message<Page<Alert>>> getAlerts(
|
||||
@Parameter(description = "Alarm ID List", example = "6565466456") @RequestParam(required = false) List<Long> ids,
|
||||
@Parameter(description = "Alarm monitor object ID", example = "6565463543") @RequestParam(required = false) Long monitorId,
|
||||
@Parameter(description = "Alarm level", example = "6565463543") @RequestParam(required = false) Byte priority,
|
||||
@Parameter(description = "Alarm Status", example = "6565463543") @RequestParam(required = false) Byte status,
|
||||
@Parameter(description = "Alarm content fuzzy query", example = "linux") @RequestParam(required = false) String content,
|
||||
@Parameter(description = "Sort field, default id", example = "name") @RequestParam(defaultValue = "id") String sort,
|
||||
@Parameter(description = "Sort Type", example = "desc") @RequestParam(defaultValue = "desc") String order,
|
||||
@Parameter(description = "List current page", example = "0") @RequestParam(defaultValue = "0") int pageIndex,
|
||||
@Parameter(description = "Number of list pagination", example = "8") @RequestParam(defaultValue = "8") int pageSize) {
|
||||
|
||||
Specification<Alert> specification = (root, query, criteriaBuilder) -> {
|
||||
List<Predicate> andList = new ArrayList<>();
|
||||
|
||||
if (ids != null && !ids.isEmpty()) {
|
||||
CriteriaBuilder.In<Long> inPredicate = criteriaBuilder.in(root.get("id"));
|
||||
for (long id : ids) {
|
||||
inPredicate.value(id);
|
||||
}
|
||||
andList.add(inPredicate);
|
||||
}
|
||||
if (monitorId != null) {
|
||||
Predicate predicate = criteriaBuilder.like(root.get("tags").as(String.class), "%" + monitorId + "%");
|
||||
andList.add(predicate);
|
||||
}
|
||||
if (priority != null) {
|
||||
Predicate predicate = criteriaBuilder.equal(root.get("priority"), priority);
|
||||
andList.add(predicate);
|
||||
}
|
||||
if (status != null) {
|
||||
Predicate predicate = criteriaBuilder.equal(root.get("status"), status);
|
||||
andList.add(predicate);
|
||||
}
|
||||
if (content != null && !content.isEmpty()) {
|
||||
Predicate predicateContent = criteriaBuilder.like(root.get("content"), "%" + content + "%");
|
||||
andList.add(predicateContent);
|
||||
}
|
||||
Predicate[] predicates = new Predicate[andList.size()];
|
||||
return criteriaBuilder.and(andList.toArray(predicates));
|
||||
};
|
||||
Sort sortExp = Sort.by(new Sort.Order(Sort.Direction.fromString(order), sort));
|
||||
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sortExp);
|
||||
Page<Alert> alertPage = alertService.getAlerts(specification, pageRequest);
|
||||
Message<Page<Alert>> message = Message.success(alertPage);
|
||||
return ResponseEntity.ok(message);
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@Operation(summary = "Delete alarms in batches", description = "according to the alarm ID list to delete the alarm information in batches")
|
||||
public ResponseEntity<Message<Void>> deleteAlerts(
|
||||
@Parameter(description = "Alarm List ID", example = "6565463543") @RequestParam(required = false) List<Long> ids) {
|
||||
if (ids != null && !ids.isEmpty()) {
|
||||
alertService.deleteAlerts(new HashSet<>(ids));
|
||||
}
|
||||
Message<Void> message = Message.success();
|
||||
return ResponseEntity.ok(message);
|
||||
}
|
||||
|
||||
@DeleteMapping("/clear")
|
||||
@Operation(summary = "Delete alarms in batches", description = "delete all alarm information")
|
||||
public ResponseEntity<Message<Void>> clearAllAlerts() {
|
||||
alertService.clearAlerts();
|
||||
Message<Void> message = Message.success();
|
||||
return ResponseEntity.ok(message);
|
||||
}
|
||||
|
||||
@PutMapping(path = "/status/{status}")
|
||||
@Operation(summary = "Batch modify alarm status, set read and unread", description = "Batch modify alarm status, set read and unread")
|
||||
public ResponseEntity<Message<Void>> applyAlertDefinesStatus(
|
||||
@Parameter(description = "Alarm status value", example = "0") @PathVariable Byte status,
|
||||
@Parameter(description = "Alarm List IDS", example = "6565463543") @RequestParam(required = false) List<Long> ids) {
|
||||
if (ids != null && status != null && !ids.isEmpty()) {
|
||||
alertService.editAlertStatus(status, ids);
|
||||
}
|
||||
Message<Void> message = Message.success();
|
||||
return ResponseEntity.ok(message);
|
||||
}
|
||||
|
||||
@GetMapping(path = "/summary")
|
||||
@Operation(summary = "Get alarm statistics", description = "Get alarm statistics information")
|
||||
public ResponseEntity<Message<AlertSummary>> getAlertsSummary() {
|
||||
AlertSummary alertSummary = alertService.getAlertsSummary();
|
||||
Message<AlertSummary> message = Message.success(alertSummary);
|
||||
return ResponseEntity.ok(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* 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.dao;
|
||||
|
||||
import java.util.Set;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertConverge;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
|
||||
/**
|
||||
* AlertConverge Dao
|
||||
*/
|
||||
public interface AlertConvergeDao extends JpaRepository<AlertConverge, Long>, JpaSpecificationExecutor<AlertConverge> {
|
||||
|
||||
/**
|
||||
* Delete alarm converge based on the ID list
|
||||
* @param convergeIds alert converge id list
|
||||
*/
|
||||
@Modifying
|
||||
void deleteAlertConvergesByIdIn(Set<Long> convergeIds);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.hertzbeat.alert.dto.AlertPriorityNum;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
/**
|
||||
* Alert Database Operations
|
||||
*/
|
||||
public interface AlertDao extends JpaRepository<Alert, Long>, JpaSpecificationExecutor<Alert> {
|
||||
|
||||
/**
|
||||
* Delete alerts based on ID list
|
||||
* @param alertIds Alert ID List
|
||||
*/
|
||||
void deleteAlertsByIdIn(Set<Long> alertIds);
|
||||
|
||||
/**
|
||||
* Updates the alarm status based on the alarm ID-status value
|
||||
* @param status status value
|
||||
* @param ids alarm ids
|
||||
*/
|
||||
@Modifying
|
||||
@Query("update Alert set status = :status where id in :ids")
|
||||
void updateAlertsStatus(@Param(value = "status") Byte status, @Param(value = "ids") List<Long> ids);
|
||||
|
||||
/**
|
||||
* Query the number of unhandled alarms of each alarm severity
|
||||
* @return List of alerts num
|
||||
*/
|
||||
@Query("select new org.apache.hertzbeat.alert.dto.AlertPriorityNum(mo.priority, count(mo.id)) from Alert mo where mo.status = 0 group by mo.priority")
|
||||
List<AlertPriorityNum> findAlertPriorityNum();
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 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.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
/**
|
||||
* AlertDefine Dao
|
||||
*/
|
||||
public interface AlertDefineDao extends JpaRepository<AlertDefine, Long>, JpaSpecificationExecutor<AlertDefine> {
|
||||
|
||||
/**
|
||||
* Delete alarm definitions based on the ID list
|
||||
* @param alertDefineIds alarm define ids
|
||||
*/
|
||||
void deleteAlertDefinesByIdIn(Set<Long> alertDefineIds);
|
||||
|
||||
/**
|
||||
* Query the default alarm thresholds based on the monitoring metrics type
|
||||
* @param app monitoring type
|
||||
* @param metric metrics
|
||||
* @return alarm defines
|
||||
*/
|
||||
List<AlertDefine> queryAlertDefinesByAppAndMetricAndPresetTrueAndEnableTrue(String app, String metric);
|
||||
|
||||
/**
|
||||
* Query app metric alert define
|
||||
* @param app app
|
||||
* @param metric metric
|
||||
* @return alert define
|
||||
*/
|
||||
List<AlertDefine> queryAlertDefineByAppAndMetric(String app, String metric);
|
||||
|
||||
/**
|
||||
* Query the alarm definition list associated with the monitoring ID
|
||||
* @param monitorId monitor id
|
||||
* @param app monitor type
|
||||
* @param metrics metrics
|
||||
* @return Alarm Definition List
|
||||
*/
|
||||
@Query("select define from AlertDefine define join AlertDefineMonitorBind bind on bind.alertDefineId = define.id "
|
||||
+ "where bind.monitorId = :monitorId and define.app = :app and define.metric = :metrics and define.enable = true and define.preset = false")
|
||||
List<AlertDefine> queryAlertDefinesByMonitor(@Param(value = "monitorId") Long monitorId,
|
||||
@Param(value = "app") String app,
|
||||
@Param(value = "metrics") String metrics);
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* 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.JsonIgnore;
|
||||
import java.util.Map;
|
||||
import org.apache.hertzbeat.common.entity.dto.AlertReport;
|
||||
|
||||
/**
|
||||
* Abstract parent of cloud service alarm information
|
||||
* {@link AlertReport} - The method corresponds to the parameter of this class one by one. For details about the method, see this class
|
||||
*/
|
||||
public abstract class CloudAlertReportAbstract {
|
||||
|
||||
/**
|
||||
* This parameter specifies the alarm name when a cloud alarm is converted to an internal alarm
|
||||
*/
|
||||
@JsonIgnore
|
||||
public abstract String getAlertName();
|
||||
|
||||
/**
|
||||
* Interval when a cloud alarm is converted to an internal alarm
|
||||
*/
|
||||
@JsonIgnore
|
||||
public abstract Integer getAlertDuration();
|
||||
|
||||
/**
|
||||
* Specifies the alarm time when a cloud alarm is converted to an internal alarm
|
||||
*/
|
||||
@JsonIgnore
|
||||
public abstract long getAlertTime();
|
||||
|
||||
/**
|
||||
* Severity of an alarm when a cloud alarm is converted to an internal alarm
|
||||
*/
|
||||
@JsonIgnore
|
||||
public abstract Integer getPriority();
|
||||
|
||||
/**
|
||||
* This parameter specifies the alarm type when a cloud alarm is converted to an internal alarm
|
||||
*/
|
||||
@JsonIgnore
|
||||
public abstract Integer getReportType();
|
||||
|
||||
/**
|
||||
* Alarm label information about a cloud alarm that is converted to an internal alarm
|
||||
*/
|
||||
@JsonIgnore
|
||||
public abstract Map<String, String> getLabels();
|
||||
|
||||
/**
|
||||
* Alarm labels when a cloud alarm is converted to an internal alarm
|
||||
*/
|
||||
@JsonIgnore
|
||||
public abstract Map<String, String> getAnnotations();
|
||||
|
||||
/**
|
||||
* This topic describes the alarm content when a cloud alarm is converted to an internal alarm
|
||||
*/
|
||||
@JsonIgnore
|
||||
public abstract String getContent();
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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 java.util.Optional;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.alert.util.DateUtil;
|
||||
import org.apache.hertzbeat.common.entity.dto.AlertReport;
|
||||
|
||||
/**
|
||||
* Generic cloud alarm entity class
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GeneralCloudAlertReport extends AlertReport {
|
||||
|
||||
/**
|
||||
* Alarm date and time
|
||||
*/
|
||||
private String alertDateTime;
|
||||
|
||||
/**
|
||||
* DATE TIME FORMAT
|
||||
*/
|
||||
private String dateTimeFormat;
|
||||
|
||||
/**
|
||||
* You can refresh the timestamp of the alarm time with enhanced properties
|
||||
*/
|
||||
public void refreshAlertTime() {
|
||||
if (getAlertTime() != 0L) {
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isNotBlank(alertDateTime)) {
|
||||
Long timeStamp = null;
|
||||
if (StringUtils.isNotBlank(dateTimeFormat)) {
|
||||
Optional<Long> tsf = DateUtil.getTimeStampFromFormat(alertDateTime, dateTimeFormat);
|
||||
boolean present = tsf.isPresent();
|
||||
if (present) {
|
||||
timeStamp = tsf.get();
|
||||
}
|
||||
}
|
||||
if (timeStamp == null) {
|
||||
Optional<Long> tsf = DateUtil.getTimeStampFromSomeFormats(alertDateTime);
|
||||
boolean present = tsf.isPresent();
|
||||
if (present) {
|
||||
timeStamp = tsf.get();
|
||||
}
|
||||
}
|
||||
if (timeStamp != null) {
|
||||
setAlertTime(timeStamp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("parse alarm time error");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
* 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.JsonProperty;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.hertzbeat.alert.util.DateUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* Tencent cloud alarm entity class
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Builder
|
||||
public class TenCloudAlertReport extends CloudAlertReportAbstract implements Serializable {
|
||||
@JsonProperty("sessionID")
|
||||
private String sessionId;
|
||||
private String alarmStatus;
|
||||
private String alarmType;
|
||||
private AlarmObjInfo alarmObjInfo;
|
||||
private AlarmPolicyInfo alarmPolicyInfo;
|
||||
private String firstOccurTime;
|
||||
private int durationTime;
|
||||
private String recoverTime;
|
||||
|
||||
/**
|
||||
* Alarm Object information
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class AlarmObjInfo {
|
||||
private String region;
|
||||
private String namespace;
|
||||
@JsonProperty("appID")
|
||||
private String appId;
|
||||
private String uin;
|
||||
private Dimensions dimensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uniform Resource ID information
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Dimensions {
|
||||
@JsonProperty("unInstanceID")
|
||||
private String unInstanceId;
|
||||
@JsonProperty("objID")
|
||||
private String objId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alarm policy information
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class AlarmPolicyInfo {
|
||||
@JsonProperty("policyID")
|
||||
private String policyId;
|
||||
private String policyType;
|
||||
private String policyName;
|
||||
@JsonProperty("policyTypeCName")
|
||||
private String policyTypeCname;
|
||||
private Conditions conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters of indicator alarms
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Conditions {
|
||||
// alarm metrics parameters
|
||||
private String metricName;
|
||||
private String metricShowName;
|
||||
private String calcType;
|
||||
private String calcValue;
|
||||
private String calcUnit;
|
||||
private String currentValue;
|
||||
private String historyValue;
|
||||
private String unit;
|
||||
private String period;
|
||||
private String periodNum;
|
||||
private String alarmNotifyType;
|
||||
private long alarmNotifyPeriod;
|
||||
|
||||
// alarm event parameters
|
||||
private String productName;
|
||||
private String productShowName;
|
||||
private String eventName;
|
||||
private String eventShowName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlertName() {
|
||||
return "TenCloud|腾讯云";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getAlertDuration() {
|
||||
return this.durationTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAlertTime() {
|
||||
|
||||
return DateUtil.getTimeStampFromFormat(getFirstOccurTime(), "yyyy-MM-dd HH:mm:ss")
|
||||
.orElse(0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPriority() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getReportType() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getLabels() {
|
||||
return Map.of("app", "TenCloud");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAnnotations() {
|
||||
return Map.of("app", "TenCloud");
|
||||
}
|
||||
|
||||
/**
|
||||
* Transaction alarm
|
||||
*/
|
||||
private static final String EVENT = "event";
|
||||
|
||||
/**
|
||||
* Indicator alarm
|
||||
*/
|
||||
private static final String METRIC = "metric";
|
||||
|
||||
/**
|
||||
* If the following alarm types increase, the entity class can be divided into a parent class and multiple subclasses, and then the method can be implemented in the subclass
|
||||
* Since there are only two, it will not be split for the time being
|
||||
*/
|
||||
@Override
|
||||
public String getContent() {
|
||||
StringBuilder contentBuilder = new StringBuilder();
|
||||
// 判断类型
|
||||
if (EVENT.equals(getAlarmType())) {
|
||||
contentBuilder
|
||||
.append("[")
|
||||
.append("告警状态 | ")
|
||||
.append("0".equals(alarmStatus) ? "恢复" : "告警")
|
||||
.append("]\n")
|
||||
.append("[")
|
||||
.append("告警对象信息 | ")
|
||||
.append(getAlarmObjInfo().getRegion() == null ? "" : "region:" + getAlarmObjInfo().getRegion())
|
||||
.append(";").append("appId:").append(getAlarmObjInfo().getAppId())
|
||||
.append(";").append("uni:").append(getAlarmObjInfo().getUin())
|
||||
.append(";").append("unInstanceId:").append(getAlarmObjInfo().getDimensions().getUnInstanceId())
|
||||
.append("]\n")
|
||||
.append("[")
|
||||
.append("告警策略组信息 | ")
|
||||
.append("名称:").append(getAlarmPolicyInfo().getPolicyName())
|
||||
.append(";")
|
||||
.append("策略类型展示名称:").append(getAlarmPolicyInfo().getConditions().getProductName())
|
||||
.append(",").append(getAlarmPolicyInfo().getConditions().getProductShowName())
|
||||
.append(";")
|
||||
.append("事件告警名称:").append(getAlarmPolicyInfo().getConditions().getEventName())
|
||||
.append(",").append(getAlarmPolicyInfo().getConditions().getEventShowName())
|
||||
.append("]");
|
||||
} else if (METRIC.equals(getAlarmType())) {
|
||||
contentBuilder
|
||||
.append("[")
|
||||
.append("告警对象:")
|
||||
.append(getAlarmObjInfo().getRegion() == null ? "" : getAlarmObjInfo().getRegion())
|
||||
.append(getAlarmObjInfo().getRegion() == null ? "" : "|")
|
||||
.append(getAlarmObjInfo().getNamespace())
|
||||
.append("]")
|
||||
.append("[")
|
||||
.append("告警内容:")
|
||||
.append(getAlarmPolicyInfo().getPolicyTypeCname()).append("|")
|
||||
.append(getAlarmPolicyInfo().getConditions().getMetricShowName()).append("|")
|
||||
.append(getAlarmPolicyInfo().getConditions().getMetricName())
|
||||
.append(getAlarmPolicyInfo().getConditions().getCalcType())
|
||||
.append(getAlarmPolicyInfo().getConditions().getCalcValue())
|
||||
.append(getAlarmPolicyInfo().getConditions().getCalcUnit())
|
||||
.append("]")
|
||||
.append("[")
|
||||
.append("当前数据")
|
||||
.append(getAlarmPolicyInfo().getConditions().getCurrentValue())
|
||||
.append(getAlarmPolicyInfo().getConditions().getCalcUnit())
|
||||
.append("]");
|
||||
}
|
||||
return contentBuilder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* 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.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.apache.hertzbeat.alert.dto.CloudAlertReportAbstract;
|
||||
import org.apache.hertzbeat.alert.dto.TenCloudAlertReport;
|
||||
|
||||
/**
|
||||
* Cloud server alarm enum
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum CloudServiceAlarmInformationEnum {
|
||||
|
||||
TencentCloud("tencloud", TenCloudAlertReport.class);
|
||||
|
||||
/**
|
||||
* cloud service name
|
||||
*/
|
||||
private final String cloudServiceName;
|
||||
|
||||
/**
|
||||
* cloud service body
|
||||
*/
|
||||
private final Class<? extends CloudAlertReportAbstract> cloudServiceAlarmInformationEntity;
|
||||
|
||||
public static CloudServiceAlarmInformationEnum getEnumFromCloudServiceName(String name) {
|
||||
return Arrays.stream(CloudServiceAlarmInformationEnum.values())
|
||||
.filter(cloudService -> cloudService.cloudServiceName.equals(name))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* 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.reduce;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dao.AlertMonitorDao;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.Tag;
|
||||
import org.apache.hertzbeat.common.queue.CommonDataQueue;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* reduce alarm and send alert data
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class AlarmCommonReduce {
|
||||
|
||||
private final AlarmSilenceReduce alarmSilenceReduce;
|
||||
|
||||
private final AlarmConvergeReduce alarmConvergeReduce;
|
||||
|
||||
private final CommonDataQueue dataQueue;
|
||||
|
||||
private final AlertMonitorDao alertMonitorDao;
|
||||
|
||||
public void reduceAndSendAlarm(Alert alert) {
|
||||
alert.setTimes(1);
|
||||
Map<String, String> tags = alert.getTags();
|
||||
if (tags == null) {
|
||||
tags = new HashMap<>(8);
|
||||
alert.setTags(tags);
|
||||
}
|
||||
String monitorIdStr = tags.get(CommonConstants.TAG_MONITOR_ID);
|
||||
if (monitorIdStr == null) {
|
||||
log.debug("receiver extern alarm message: {}", alert);
|
||||
} else {
|
||||
long monitorId = Long.parseLong(monitorIdStr);
|
||||
List<Tag> tagList = alertMonitorDao.findMonitorIdBindTags(monitorId);
|
||||
for (Tag tag : tagList) {
|
||||
if (!tags.containsKey(tag.getName())) {
|
||||
tags.put(tag.getName(), tag.getTagValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
// converge -> silence
|
||||
if (alarmConvergeReduce.filterConverge(alert) && alarmSilenceReduce.filterSilence(alert)) {
|
||||
dataQueue.sendAlertsData(alert);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
* 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.reduce;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.apache.hertzbeat.alert.dao.AlertConvergeDao;
|
||||
import org.apache.hertzbeat.common.cache.CacheFactory;
|
||||
import org.apache.hertzbeat.common.cache.CommonCacheService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertConverge;
|
||||
import org.apache.hertzbeat.common.entity.manager.TagItem;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* alarm converge
|
||||
*/
|
||||
@Service
|
||||
public class AlarmConvergeReduce {
|
||||
|
||||
private final AlertConvergeDao alertConvergeDao;
|
||||
|
||||
private final Map<Integer, Alert> converageAlertMap;
|
||||
|
||||
public AlarmConvergeReduce(AlertConvergeDao alertConvergeDao) {
|
||||
this.alertConvergeDao = alertConvergeDao;
|
||||
this.converageAlertMap = new ConcurrentHashMap<>(16);
|
||||
}
|
||||
|
||||
/**
|
||||
* currentAlert converge filter data
|
||||
*
|
||||
* @param currentAlert currentAlert
|
||||
* @return true when not filter
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean filterConverge(Alert currentAlert) {
|
||||
// ignore monitor status auto recover notice
|
||||
if (currentAlert.getTags() != null && currentAlert.getTags().containsKey(CommonConstants.IGNORE)) {
|
||||
return true;
|
||||
}
|
||||
if (currentAlert.getStatus() == CommonConstants.ALERT_STATUS_CODE_RESTORED) {
|
||||
// restored alert
|
||||
int alertHash = Objects.hash(CommonConstants.ALERT_PRIORITY_CODE_CRITICAL)
|
||||
+ Arrays.hashCode(currentAlert.getTags().keySet().toArray(new String[0]))
|
||||
+ Arrays.hashCode(currentAlert.getTags().values().toArray(new String[0]));
|
||||
converageAlertMap.remove(alertHash);
|
||||
alertHash = Objects.hash(CommonConstants.ALERT_PRIORITY_CODE_EMERGENCY)
|
||||
+ Arrays.hashCode(currentAlert.getTags().keySet().toArray(new String[0]))
|
||||
+ Arrays.hashCode(currentAlert.getTags().values().toArray(new String[0]));
|
||||
converageAlertMap.remove(alertHash);
|
||||
alertHash = Objects.hash(CommonConstants.ALERT_PRIORITY_CODE_WARNING)
|
||||
+ Arrays.hashCode(currentAlert.getTags().keySet().toArray(new String[0]))
|
||||
+ Arrays.hashCode(currentAlert.getTags().values().toArray(new String[0]));
|
||||
converageAlertMap.remove(alertHash);
|
||||
return true;
|
||||
}
|
||||
CommonCacheService<String, Object> convergeCache = CacheFactory.getAlertConvergeCache();
|
||||
List<AlertConverge> alertConvergeList = (List<AlertConverge>) convergeCache.get(CommonConstants.CACHE_ALERT_CONVERGE);
|
||||
if (alertConvergeList == null) {
|
||||
alertConvergeList = alertConvergeDao.findAll();
|
||||
// matchAll is in the last
|
||||
alertConvergeList.sort((item1, item2) -> {
|
||||
if (item1.isMatchAll()) {
|
||||
return 1;
|
||||
} else if (item2.isMatchAll()) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
convergeCache.put(CommonConstants.CACHE_ALERT_CONVERGE, alertConvergeList);
|
||||
}
|
||||
for (AlertConverge alertConverge : alertConvergeList) {
|
||||
if (!alertConverge.isEnable()) {
|
||||
continue;
|
||||
}
|
||||
boolean match = alertConverge.isMatchAll();
|
||||
if (!match) {
|
||||
List<TagItem> tags = alertConverge.getTags();
|
||||
if (currentAlert.getTags() != null && !currentAlert.getTags().isEmpty()) {
|
||||
Map<String, String> alertTagMap = currentAlert.getTags();
|
||||
match = tags.stream().anyMatch(item -> {
|
||||
if (alertTagMap.containsKey(item.getName())) {
|
||||
String tagValue = alertTagMap.get(item.getName());
|
||||
if (tagValue == null && item.getValue() == null) {
|
||||
return true;
|
||||
} else {
|
||||
return tagValue != null && tagValue.equals(item.getValue());
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
match = true;
|
||||
}
|
||||
if (match && alertConverge.getPriorities() != null && !alertConverge.getPriorities().isEmpty()) {
|
||||
match = alertConverge.getPriorities().stream().anyMatch(item -> item != null && item == currentAlert.getPriority());
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
long evalInterval = alertConverge.getEvalInterval() * 1000L;
|
||||
long now = System.currentTimeMillis();
|
||||
if (evalInterval <= 0) {
|
||||
return true;
|
||||
}
|
||||
int alertHash = Objects.hash(currentAlert.getPriority())
|
||||
+ Arrays.hashCode(currentAlert.getTags().keySet().toArray(new String[0]))
|
||||
+ Arrays.hashCode(currentAlert.getTags().values().toArray(new String[0]));
|
||||
Alert preAlert = converageAlertMap.get(alertHash);
|
||||
if (preAlert == null) {
|
||||
currentAlert.setTimes(1);
|
||||
currentAlert.setFirstAlarmTime(now);
|
||||
currentAlert.setLastAlarmTime(now);
|
||||
converageAlertMap.put(alertHash, currentAlert.clone());
|
||||
return true;
|
||||
} else {
|
||||
if (now - preAlert.getFirstAlarmTime() < evalInterval) {
|
||||
preAlert.setTimes(preAlert.getTimes() + 1);
|
||||
preAlert.setLastAlarmTime(now);
|
||||
return false;
|
||||
} else {
|
||||
currentAlert.setTimes(preAlert.getTimes());
|
||||
if (preAlert.getTimes() == 1) {
|
||||
currentAlert.setFirstAlarmTime(now);
|
||||
} else {
|
||||
currentAlert.setFirstAlarmTime(preAlert.getFirstAlarmTime());
|
||||
}
|
||||
currentAlert.setLastAlarmTime(now);
|
||||
preAlert.setFirstAlarmTime(now);
|
||||
preAlert.setLastAlarmTime(now);
|
||||
preAlert.setTimes(1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* 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.reduce;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.hertzbeat.alert.dao.AlertSilenceDao;
|
||||
import org.apache.hertzbeat.common.cache.CacheFactory;
|
||||
import org.apache.hertzbeat.common.cache.CommonCacheService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertSilence;
|
||||
import org.apache.hertzbeat.common.entity.manager.TagItem;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* silence alarm
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AlarmSilenceReduce {
|
||||
|
||||
private final AlertSilenceDao alertSilenceDao;
|
||||
|
||||
/**
|
||||
* alert silence filter data
|
||||
* @param alert alert
|
||||
* @return true when not filter
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean filterSilence(Alert alert) {
|
||||
CommonCacheService<String, Object> silenceCache = CacheFactory.getAlertSilenceCache();
|
||||
List<AlertSilence> alertSilenceList = (List<AlertSilence>) silenceCache.get(CommonConstants.CACHE_ALERT_SILENCE);
|
||||
if (alertSilenceList == null) {
|
||||
alertSilenceList = alertSilenceDao.findAll();
|
||||
silenceCache.put(CommonConstants.CACHE_ALERT_SILENCE, alertSilenceList);
|
||||
}
|
||||
for (AlertSilence alertSilence : alertSilenceList) {
|
||||
if (!alertSilence.isEnable()) {
|
||||
continue;
|
||||
}
|
||||
// if match the silence rule, return
|
||||
boolean match = alertSilence.isMatchAll();
|
||||
if (!match) {
|
||||
List<TagItem> tags = alertSilence.getTags();
|
||||
if (alert.getTags() != null && !alert.getTags().isEmpty()) {
|
||||
Map<String, String> alertTagMap = alert.getTags();
|
||||
match = tags.stream().anyMatch(item -> {
|
||||
if (alertTagMap.containsKey(item.getName())) {
|
||||
String tagValue = alertTagMap.get(item.getName());
|
||||
if (tagValue == null && item.getValue() == null) {
|
||||
return true;
|
||||
} else {
|
||||
return tagValue != null && tagValue.equals(item.getValue());
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
match = true;
|
||||
}
|
||||
if (match && alertSilence.getPriorities() != null && !alertSilence.getPriorities().isEmpty()) {
|
||||
match = alertSilence.getPriorities().stream().anyMatch(item -> item != null && item == alert.getPriority());
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
LocalDateTime nowDate = LocalDateTime.now();
|
||||
if (alertSilence.getType() == 0) {
|
||||
// once time
|
||||
boolean startMatch = alertSilence.getPeriodStart() == null || nowDate.isAfter(alertSilence.getPeriodStart().toLocalDateTime());
|
||||
boolean endMatch = alertSilence.getPeriodEnd() == null || nowDate.isBefore(alertSilence.getPeriodEnd().toLocalDateTime());
|
||||
if (startMatch && endMatch) {
|
||||
int times = Optional.ofNullable(alertSilence.getTimes()).orElse(0);
|
||||
alertSilence.setTimes(times + 1);
|
||||
alertSilenceDao.save(alertSilence);
|
||||
return false;
|
||||
}
|
||||
} else if (alertSilence.getType() == 1) {
|
||||
// cyc time
|
||||
int currentDayOfWeek = nowDate.toLocalDate().getDayOfWeek().getValue();
|
||||
if (alertSilence.getDays() != null && !alertSilence.getDays().isEmpty()) {
|
||||
boolean dayMatch = alertSilence.getDays().stream().anyMatch(item -> item == currentDayOfWeek);
|
||||
if (dayMatch) {
|
||||
LocalTime nowTime = nowDate.toLocalTime();
|
||||
boolean startMatch = alertSilence.getPeriodStart() == null || nowTime.isAfter(alertSilence.getPeriodStart().toLocalTime());
|
||||
boolean endMatch = alertSilence.getPeriodEnd() == null || nowTime.isBefore(alertSilence.getPeriodEnd().toLocalTime());
|
||||
if (startMatch && endMatch) {
|
||||
int times = Optional.ofNullable(alertSilence.getTimes()).orElse(0);
|
||||
alertSilence.setTimes(times + 1);
|
||||
alertSilenceDao.save(alertSilence);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* 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 java.util.Set;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertConverge;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
* management interface service for alert converge
|
||||
*/
|
||||
public interface AlertConvergeService {
|
||||
/**
|
||||
* Verify the correctness of the request data parameters
|
||||
* @param alertConverge AlertConverge
|
||||
* @param isModify whether modify
|
||||
* @throws IllegalArgumentException A checksum parameter error is thrown
|
||||
*/
|
||||
void validate(AlertConverge alertConverge, boolean isModify) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* New AlertConverge
|
||||
* @param alertConverge AlertConverge Entity
|
||||
* @throws RuntimeException Added procedure exception throwing
|
||||
*/
|
||||
void addAlertConverge(AlertConverge alertConverge) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Modifying an AlertConverge
|
||||
* @param alertConverge Alarm definition Entity
|
||||
* @throws RuntimeException Exception thrown during modification
|
||||
*/
|
||||
void modifyAlertConverge(AlertConverge alertConverge) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Obtain AlertConverge information
|
||||
* @param convergeId AlertConverge ID
|
||||
* @return AlertConverge
|
||||
* @throws RuntimeException An exception was thrown during the query
|
||||
*/
|
||||
AlertConverge getAlertConverge(long convergeId) throws RuntimeException;
|
||||
|
||||
|
||||
/**
|
||||
* Delete AlertConverge in batches
|
||||
* @param convergeIds AlertConverge IDs
|
||||
* @throws RuntimeException Exception thrown during deletion
|
||||
*/
|
||||
void deleteAlertConverges(Set<Long> convergeIds) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param specification Query conditions
|
||||
* @param pageRequest Paging parameters
|
||||
* @return The query results
|
||||
*/
|
||||
Page<AlertConverge> getAlertConverges(Specification<AlertConverge> specification, PageRequest pageRequest);
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* 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 jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefineMonitorBind;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* Alarm define manager service
|
||||
*/
|
||||
public interface AlertDefineService {
|
||||
|
||||
/**
|
||||
* Verify the correctness of the request data parameters
|
||||
* @param alertDefine alertDefine
|
||||
* @param isModify whether modify
|
||||
* @throws IllegalArgumentException A checksum parameter error is thrown
|
||||
*/
|
||||
void validate(AlertDefine alertDefine, boolean isModify) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* New Alarm Definition
|
||||
* @param alertDefine Alarm definition Entity
|
||||
* @throws RuntimeException Added procedure exception throwing
|
||||
*/
|
||||
void addAlertDefine(AlertDefine alertDefine) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Modifying an Alarm Definition
|
||||
* @param alertDefine Alarm definition Entity
|
||||
* @throws RuntimeException Exception thrown during modification
|
||||
*/
|
||||
void modifyAlertDefine(AlertDefine alertDefine) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Deleting an Alarm Definition
|
||||
* @param alertId Alarm Definition ID
|
||||
* @throws RuntimeException Exception thrown during deletion
|
||||
*/
|
||||
void deleteAlertDefine(long alertId) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Obtain alarm definition information
|
||||
* @param alertId Monitor the ID
|
||||
* @return AlertDefine
|
||||
* @throws RuntimeException An exception was thrown during the query
|
||||
*/
|
||||
AlertDefine getAlertDefine(long alertId) throws RuntimeException;
|
||||
|
||||
|
||||
/**
|
||||
* Delete alarm definitions in batches
|
||||
* @param alertIds Alarm Definition IDs
|
||||
* @throws RuntimeException Exception thrown during deletion
|
||||
*/
|
||||
void deleteAlertDefines(Set<Long> alertIds) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param specification Query conditions
|
||||
* @param pageRequest Paging parameters
|
||||
* @return The query results
|
||||
*/
|
||||
Page<AlertDefine> getMonitorBindAlertDefines(Specification<AlertDefine> specification, PageRequest pageRequest);
|
||||
|
||||
/**
|
||||
* Association between application alarm schedule and monitoring
|
||||
* @param alertId Alarm Definition ID
|
||||
* @param alertDefineBinds correlation
|
||||
*/
|
||||
void applyBindAlertDefineMonitors(Long alertId, List<AlertDefineMonitorBind> alertDefineBinds);
|
||||
|
||||
/**
|
||||
* Query the alarm definitions that match the specified metrics associated with the monitoring ID
|
||||
* @param monitorId Monitor the ID
|
||||
* @param app Monitoring type
|
||||
* @param metrics metrics
|
||||
* @return field - define[]
|
||||
*/
|
||||
Map<String, List<AlertDefine>> getMonitorBindAlertDefines(long monitorId, String app, String metrics);
|
||||
|
||||
/**
|
||||
* Query the alarm definitions that match the specified metrics associated with the monitoring ID
|
||||
* @param monitorId Monitor the ID
|
||||
* @param app Monitoring type
|
||||
* @param metrics metrics
|
||||
* @return field - define[]
|
||||
*/
|
||||
AlertDefine getMonitorBindAlertAvaDefine(long monitorId, String app, String metrics);
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param specification Query conditions
|
||||
* @param pageRequest Paging parameters
|
||||
* @return The query results
|
||||
*/
|
||||
Page<AlertDefine> getAlertDefines(Specification<AlertDefine> specification, PageRequest pageRequest);
|
||||
|
||||
/**
|
||||
* Query the associated monitoring list information based on the alarm definition ID
|
||||
* @param alertDefineId Alarm Definition ID
|
||||
* @return Associated information about the monitoring list
|
||||
*/
|
||||
List<AlertDefineMonitorBind> getBindAlertDefineMonitors(long alertDefineId);
|
||||
|
||||
/**
|
||||
* Export file configuration of specified type based on ID list and export file type
|
||||
* @param ids AlertDefine ID
|
||||
* @param type File Type
|
||||
* @param res Response
|
||||
* @throws Exception An exception was thrown during the export
|
||||
*/
|
||||
void export(List<Long> ids, String type, HttpServletResponse res) throws Exception;
|
||||
|
||||
/**
|
||||
* Add alarm threshold rules based on the uploaded alarm threshold file
|
||||
* @param file Upload File
|
||||
* @throws Exception An exception was thrown during the importConfig
|
||||
*/
|
||||
void importConfig(MultipartFile file) throws Exception;
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* 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 java.util.HashSet;
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.alert.dto.AlertSummary;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.dto.AlertReport;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
* Alarm information management interface
|
||||
*/
|
||||
public interface AlertService {
|
||||
|
||||
/**
|
||||
* Add alarm record
|
||||
* @param alert Alert entity
|
||||
* @throws RuntimeException Add process exception throw
|
||||
*/
|
||||
void addAlert(Alert alert) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param specification Query conditions
|
||||
* @param pageRequest pagination parameters
|
||||
* @return search result
|
||||
*/
|
||||
Page<Alert> getAlerts(Specification<Alert> specification, PageRequest pageRequest);
|
||||
|
||||
/**
|
||||
* Delete alarms in batches according to the alarm ID list
|
||||
* @param ids Alarm ID List
|
||||
*/
|
||||
void deleteAlerts(HashSet<Long> ids);
|
||||
|
||||
/**
|
||||
* Clear all alerts
|
||||
*/
|
||||
void clearAlerts();
|
||||
|
||||
/**
|
||||
* Update the alarm status according to the alarm ID-status value
|
||||
* @param status Alarm status to be modified
|
||||
* @param ids Alarm ID List to be modified
|
||||
*/
|
||||
void editAlertStatus(Byte status, List<Long> ids);
|
||||
|
||||
/**
|
||||
* Get alarm statistics information
|
||||
* @return Alarm statistics information
|
||||
*/
|
||||
AlertSummary getAlertsSummary();
|
||||
|
||||
/**
|
||||
* A third party reports an alarm
|
||||
* @param alertReport The alarm information
|
||||
*/
|
||||
void addNewAlertReport(AlertReport alertReport);
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param specification Query conditions
|
||||
* @return search result
|
||||
*/
|
||||
List<Alert> getAlerts(Specification<Alert> specification);
|
||||
}
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import java.util.Set;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dao.AlertConvergeDao;
|
||||
import org.apache.hertzbeat.alert.service.AlertConvergeService;
|
||||
import org.apache.hertzbeat.common.cache.CacheFactory;
|
||||
import org.apache.hertzbeat.common.cache.CommonCacheService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertConverge;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* implement for alert converge service
|
||||
*/
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Slf4j
|
||||
public class AlertConvergeServiceImpl implements AlertConvergeService {
|
||||
|
||||
@Autowired
|
||||
private AlertConvergeDao alertConvergeDao;
|
||||
|
||||
@Override
|
||||
public void validate(AlertConverge alertConverge, boolean isModify) throws IllegalArgumentException {
|
||||
// todo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAlertConverge(AlertConverge alertConverge) throws RuntimeException {
|
||||
alertConvergeDao.save(alertConverge);
|
||||
clearAlertConvergesCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyAlertConverge(AlertConverge alertConverge) throws RuntimeException {
|
||||
alertConvergeDao.save(alertConverge);
|
||||
clearAlertConvergesCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertConverge getAlertConverge(long convergeId) throws RuntimeException {
|
||||
return alertConvergeDao.findById(convergeId).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAlertConverges(Set<Long> convergeIds) throws RuntimeException {
|
||||
alertConvergeDao.deleteAlertConvergesByIdIn(convergeIds);
|
||||
clearAlertConvergesCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<AlertConverge> getAlertConverges(Specification<AlertConverge> specification, PageRequest pageRequest) {
|
||||
return alertConvergeDao.findAll(specification, pageRequest);
|
||||
}
|
||||
|
||||
private void clearAlertConvergesCache() {
|
||||
CommonCacheService<String, Object> convergeCache = CacheFactory.getAlertConvergeCache();
|
||||
convergeCache.remove(CommonConstants.CACHE_ALERT_CONVERGE);
|
||||
}
|
||||
}
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* 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 cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import cn.afterturn.easypoi.excel.annotation.ExcelTarget;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineImExportService;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.manager.TagItem;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Configuration Import Export
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AlertDefineAbstractImExportServiceImpl implements AlertDefineImExportService {
|
||||
@Resource
|
||||
@Lazy
|
||||
private AlertDefineService alertDefineService;
|
||||
|
||||
@Override
|
||||
public void importConfig(InputStream is) {
|
||||
var formList = parseImport(is)
|
||||
.stream()
|
||||
.map(this::convert)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
if (!CollectionUtils.isEmpty(formList)) {
|
||||
formList.forEach(alertDefine -> {
|
||||
alertDefineService.validate(alertDefine, false);
|
||||
alertDefineService.addAlertDefine(alertDefine);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportConfig(OutputStream os, List<Long> configList) {
|
||||
var monitorList = configList.stream()
|
||||
.map(it -> alertDefineService.getAlertDefine(it))
|
||||
.map(this::convert)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
writeOs(monitorList, os);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parsing an input stream into a form
|
||||
*
|
||||
* @param is input stream
|
||||
* @return form list
|
||||
*/
|
||||
abstract List<ExportAlertDefineDTO> parseImport(InputStream is);
|
||||
|
||||
/**
|
||||
* Export Configuration to Output Stream
|
||||
* @param exportAlertDefineList configuration list
|
||||
* @param os output stream
|
||||
*/
|
||||
abstract void writeOs(List<ExportAlertDefineDTO> exportAlertDefineList, OutputStream os);
|
||||
|
||||
|
||||
private ExportAlertDefineDTO convert(AlertDefine alertDefine) {
|
||||
var exportAlertDefine = new ExportAlertDefineDTO();
|
||||
var alertDefineDTO = new AlertDefineDTO();
|
||||
BeanUtils.copyProperties(alertDefine, alertDefineDTO);
|
||||
exportAlertDefine.setAlertDefine(alertDefineDTO);
|
||||
return exportAlertDefine;
|
||||
}
|
||||
|
||||
private AlertDefine convert(ExportAlertDefineDTO exportAlertDefineDTO) {
|
||||
var alertDefine = new AlertDefine();
|
||||
var alertDefineDTO = exportAlertDefineDTO.getAlertDefine();
|
||||
BeanUtils.copyProperties(alertDefineDTO, alertDefine);
|
||||
return alertDefine;
|
||||
}
|
||||
|
||||
protected String fileNamePrefix() {
|
||||
return "hertzbeat_alertDefine_" + LocalDate.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Export data transfer objects for alert configurations
|
||||
*/
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@ExcelTarget(value = "ExportAlertDefineDTO")
|
||||
protected static class ExportAlertDefineDTO {
|
||||
@Excel(name = "AlertDefine")
|
||||
private AlertDefineDTO alertDefine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data transfer object for alert configuration
|
||||
*/
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@ExcelTarget(value = "AlertDefineDTO")
|
||||
protected static class AlertDefineDTO {
|
||||
@Excel(name = "App")
|
||||
private String app;
|
||||
@Excel(name = "Metric")
|
||||
private String metric;
|
||||
@Excel(name = "Field")
|
||||
private String field;
|
||||
@Excel(name = "Preset")
|
||||
private Boolean preset;
|
||||
@Excel(name = "Expr")
|
||||
private String expr;
|
||||
@Excel(name = "Priority")
|
||||
private Byte priority;
|
||||
@Excel(name = "Times")
|
||||
private Integer times;
|
||||
@Excel(name = "Tags")
|
||||
private List<TagItem> tags;
|
||||
@Excel(name = "Enable")
|
||||
private Boolean enable;
|
||||
@Excel(name = "RecoverNotice")
|
||||
private Boolean recoverNotice;
|
||||
@Excel(name = "Template")
|
||||
private String template;
|
||||
}
|
||||
}
|
||||
-265
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
* 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 com.fasterxml.jackson.core.type.TypeReference;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.entity.manager.TagItem;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.CellType;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Configure the import and export EXCEL format
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AlertDefineExcelImExportServiceImpl extends AlertDefineAbstractImExportServiceImpl {
|
||||
public static final String TYPE = "EXCEL";
|
||||
public static final String FILE_SUFFIX = ".xlsx";
|
||||
|
||||
/**
|
||||
* Export file type
|
||||
* @return file type
|
||||
*/
|
||||
@Override
|
||||
public String type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Export File Name
|
||||
* @return file name
|
||||
*/
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return fileNamePrefix() + FILE_SUFFIX;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parsing an input stream into a form
|
||||
* @param is input stream
|
||||
* @return form list
|
||||
*/
|
||||
@Override
|
||||
List<ExportAlertDefineDTO> parseImport(InputStream is) {
|
||||
try (Workbook workbook = WorkbookFactory.create(is)) {
|
||||
Sheet sheet = workbook.getSheetAt(0);
|
||||
List<ExportAlertDefineDTO> alertDefines = new ArrayList<>();
|
||||
for (Row row : sheet) {
|
||||
if (row.getRowNum() == 0) {
|
||||
continue;
|
||||
}
|
||||
String app = getCellValueAsString(row.getCell(0));
|
||||
if (StringUtils.hasText(app)) {
|
||||
AlertDefineDTO alertDefineDTO = extractAlertDefineDataFromRow(row);
|
||||
ExportAlertDefineDTO exportAlertDefineDTO = new ExportAlertDefineDTO();
|
||||
exportAlertDefineDTO.setAlertDefine(alertDefineDTO);
|
||||
alertDefines.add(exportAlertDefineDTO);
|
||||
}
|
||||
}
|
||||
return alertDefines;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to parse alertDefine data", e);
|
||||
}
|
||||
}
|
||||
|
||||
private TagItem extractTagDataFromRow(Row row) {
|
||||
String name = getCellValueAsString(row.getCell(7));
|
||||
if (StringUtils.hasText(name)) {
|
||||
TagItem tagItem = new TagItem();
|
||||
tagItem.setName(name);
|
||||
tagItem.setValue(getCellValueAsString(row.getCell(8)));
|
||||
return tagItem;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<TagItem> extractTagDataFromRow(Cell cell) {
|
||||
String jsonStr = getCellValueAsString(cell);
|
||||
if (StringUtils.hasText(jsonStr)) {
|
||||
return JsonUtil.fromJson(jsonStr, new TypeReference<>() {
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private String getCellValueAsString(Cell cell) {
|
||||
if (cell == null) {
|
||||
return null;
|
||||
}
|
||||
return switch (cell.getCellType()) {
|
||||
case STRING -> cell.getStringCellValue();
|
||||
case NUMERIC -> String.valueOf(cell.getNumericCellValue());
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private boolean getCellValueAsBoolean(Cell cell) {
|
||||
if (cell == null) {
|
||||
return false;
|
||||
}
|
||||
if (Objects.requireNonNull(cell.getCellType()) == CellType.BOOLEAN) {
|
||||
return cell.getBooleanCellValue();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private Integer getCellValueAsInteger(Cell cell) {
|
||||
if (cell == null) {
|
||||
return null;
|
||||
}
|
||||
if (Objects.requireNonNull(cell.getCellType()) == CellType.NUMERIC) {
|
||||
return (int) cell.getNumericCellValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private Byte getCellValueAsByte(Cell cell) {
|
||||
if (cell == null) {
|
||||
return null;
|
||||
}
|
||||
if (Objects.requireNonNull(cell.getCellType()) == CellType.NUMERIC) {
|
||||
return (byte) cell.getNumericCellValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private AlertDefineDTO extractAlertDefineDataFromRow(Row row) {
|
||||
AlertDefineDTO alertDefineDTO = new AlertDefineDTO();
|
||||
alertDefineDTO.setApp(getCellValueAsString(row.getCell(0)));
|
||||
alertDefineDTO.setMetric(getCellValueAsString(row.getCell(1)));
|
||||
alertDefineDTO.setField(getCellValueAsString(row.getCell(2)));
|
||||
alertDefineDTO.setPreset(getCellValueAsBoolean(row.getCell(3)));
|
||||
alertDefineDTO.setExpr(getCellValueAsString(row.getCell(4)));
|
||||
alertDefineDTO.setPriority(getCellValueAsByte(row.getCell(5)));
|
||||
alertDefineDTO.setTimes(getCellValueAsInteger(row.getCell(6)));
|
||||
alertDefineDTO.setTags(extractTagDataFromRow(row.getCell(7)));
|
||||
alertDefineDTO.setEnable(getCellValueAsBoolean(row.getCell(8)));
|
||||
alertDefineDTO.setRecoverNotice(getCellValueAsBoolean(row.getCell(9)));
|
||||
alertDefineDTO.setTemplate(getCellValueAsString(row.getCell(10)));
|
||||
return alertDefineDTO;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Export Configuration to Output Stream
|
||||
* @param exportAlertDefineList exportAlertDefineList
|
||||
* @param os output stream
|
||||
*/
|
||||
@Override
|
||||
void writeOs(List<ExportAlertDefineDTO> exportAlertDefineList, OutputStream os) {
|
||||
try {
|
||||
Workbook workbook = WorkbookFactory.create(true);
|
||||
String sheetName = "Export AlertDefine";
|
||||
Sheet sheet = workbook.createSheet(sheetName);
|
||||
sheet.setDefaultColumnWidth(20);
|
||||
sheet.setColumnWidth(9, 40 * 256);
|
||||
sheet.setColumnWidth(10, 40 * 256);
|
||||
// set header style
|
||||
CellStyle headerCellStyle = workbook.createCellStyle();
|
||||
Font headerFont = workbook.createFont();
|
||||
headerFont.setBold(true);
|
||||
headerCellStyle.setFont(headerFont);
|
||||
headerCellStyle.setAlignment(HorizontalAlignment.CENTER);
|
||||
// set cell style
|
||||
CellStyle cellStyle = workbook.createCellStyle();
|
||||
cellStyle.setAlignment(HorizontalAlignment.CENTER);
|
||||
// set header
|
||||
String[] headers = {"app", "metric", "field", "preset", "expr", "priority", "times", "tags",
|
||||
"enable", "recoverNotice", "template"};
|
||||
Row headerRow = sheet.createRow(0);
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell cell = headerRow.createCell(i);
|
||||
cell.setCellValue(headers[i]);
|
||||
cell.setCellStyle(headerCellStyle);
|
||||
}
|
||||
|
||||
// Traverse the threshold rule list, each threshold rule object corresponds to a row of data
|
||||
int rowIndex = 1;
|
||||
for (ExportAlertDefineDTO alertDefine : exportAlertDefineList) {
|
||||
AlertDefineDTO alertDefineDTO = alertDefine.getAlertDefine();
|
||||
Row row = sheet.createRow(rowIndex++);
|
||||
// Threshold rule information only needs to be written once
|
||||
Cell appCell = row.createCell(0);
|
||||
appCell.setCellValue(alertDefineDTO.getApp());
|
||||
appCell.setCellStyle(cellStyle);
|
||||
Cell metricCell = row.createCell(1);
|
||||
metricCell.setCellValue(alertDefineDTO.getMetric());
|
||||
metricCell.setCellStyle(cellStyle);
|
||||
Cell fieldCell = row.createCell(2);
|
||||
fieldCell.setCellValue(alertDefineDTO.getField());
|
||||
fieldCell.setCellStyle(cellStyle);
|
||||
Cell presetCell = row.createCell(3);
|
||||
presetCell.setCellValue(alertDefineDTO.getPreset() != null
|
||||
&& alertDefineDTO.getPreset());
|
||||
presetCell.setCellStyle(cellStyle);
|
||||
Cell exprCell = row.createCell(4);
|
||||
exprCell.setCellValue(alertDefineDTO.getExpr());
|
||||
exprCell.setCellStyle(cellStyle);
|
||||
Cell priorityCell = row.createCell(5);
|
||||
priorityCell.setCellValue(alertDefineDTO.getPriority());
|
||||
priorityCell.setCellStyle(cellStyle);
|
||||
Cell timesCell = row.createCell(6);
|
||||
timesCell.setCellValue(alertDefineDTO.getTimes());
|
||||
Cell tagCell = row.createCell(7);
|
||||
// get tags
|
||||
List<TagItem> tagList = alertDefineDTO.getTags();
|
||||
String tagValue = tagList == null || tagList.isEmpty() ? "" : JsonUtil.toJson(tagList);
|
||||
tagCell.setCellValue(tagValue);
|
||||
tagCell.setCellStyle(cellStyle);
|
||||
Cell enableCell = row.createCell(8);
|
||||
enableCell.setCellValue(alertDefineDTO.getEnable() != null
|
||||
&& alertDefineDTO.getEnable());
|
||||
enableCell.setCellStyle(cellStyle);
|
||||
Cell recoverNoticeCell = row.createCell(9);
|
||||
recoverNoticeCell.setCellValue(alertDefineDTO.getRecoverNotice() != null
|
||||
&& alertDefineDTO.getRecoverNotice());
|
||||
recoverNoticeCell.setCellStyle(cellStyle);
|
||||
Cell templateCell = row.createCell(10);
|
||||
templateCell.setCellValue(alertDefineDTO.getTemplate());
|
||||
recoverNoticeCell.setCellStyle(cellStyle);
|
||||
}
|
||||
workbook.write(os);
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
-189
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* 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 jakarta.servlet.http.HttpServletResponse;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDefineBindDao;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDefineDao;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineImExportService;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefineMonitorBind;
|
||||
import org.apache.hertzbeat.common.util.JexlExpressionRunner;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* Alarm definition management interface implementation
|
||||
*/
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Slf4j
|
||||
public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
|
||||
@Autowired
|
||||
private AlertDefineDao alertDefineDao;
|
||||
|
||||
@Autowired
|
||||
private AlertDefineBindDao alertDefineBindDao;
|
||||
|
||||
private final Map<String, AlertDefineImExportService> alertDefineImExportServiceMap = new HashMap<>();
|
||||
|
||||
public AlertDefineServiceImpl(List<AlertDefineImExportService> alertDefineImExportServiceList) {
|
||||
alertDefineImExportServiceList.forEach(it -> alertDefineImExportServiceMap.put(it.type(), it));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(AlertDefine alertDefine, boolean isModify) throws IllegalArgumentException {
|
||||
// todo
|
||||
if (StringUtils.hasText(alertDefine.getExpr())) {
|
||||
try {
|
||||
JexlExpressionRunner.compile(alertDefine.getExpr());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("alert expr error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAlertDefine(AlertDefine alertDefine) throws RuntimeException {
|
||||
alertDefineDao.save(alertDefine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyAlertDefine(AlertDefine alertDefine) throws RuntimeException {
|
||||
alertDefineDao.save(alertDefine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAlertDefine(long alertId) throws RuntimeException {
|
||||
alertDefineDao.deleteById(alertId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDefine getAlertDefine(long alertId) throws RuntimeException {
|
||||
Optional<AlertDefine> optional = alertDefineDao.findById(alertId);
|
||||
return optional.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAlertDefines(Set<Long> alertIds) throws RuntimeException {
|
||||
alertDefineDao.deleteAlertDefinesByIdIn(alertIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<AlertDefine> getMonitorBindAlertDefines(Specification<AlertDefine> specification, PageRequest pageRequest) {
|
||||
return alertDefineDao.findAll(specification, pageRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBindAlertDefineMonitors(Long alertId, List<AlertDefineMonitorBind> alertDefineBinds) {
|
||||
// todo checks whether the alarm definition and monitoring exist
|
||||
if (!alertDefineBindDao.existsById(alertId)){
|
||||
alertDefineBindDao.deleteAlertDefineBindsByAlertDefineIdEquals(alertId);
|
||||
}
|
||||
// Delete all associations of this alarm
|
||||
alertDefineBindDao.deleteAlertDefineBindsByAlertDefineIdEquals(alertId);
|
||||
// Save the associated
|
||||
alertDefineBindDao.saveAll(alertDefineBinds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<AlertDefine>> getMonitorBindAlertDefines(long monitorId, String app, String metrics) {
|
||||
List<AlertDefine> defines = alertDefineDao.queryAlertDefinesByMonitor(monitorId, app, metrics);
|
||||
List<AlertDefine> defaultDefines = alertDefineDao.queryAlertDefinesByAppAndMetricAndPresetTrueAndEnableTrue(app, metrics);
|
||||
defines.addAll(defaultDefines);
|
||||
Set<AlertDefine> defineSet = defines.stream().filter(item -> item.getField() != null).collect(Collectors.toSet());
|
||||
// The alarm thresholds are defined in ascending order of the alarm severity from 0 to 3.
|
||||
// The lower the number, the higher the alarm is. That is, the alarm is calculated from the highest alarm threshold
|
||||
return defineSet.stream().sorted(Comparator.comparing(AlertDefine::getPriority))
|
||||
.collect(Collectors.groupingBy(AlertDefine::getField));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDefine getMonitorBindAlertAvaDefine(long monitorId, String app, String metrics) {
|
||||
List<AlertDefine> defines = alertDefineDao.queryAlertDefinesByMonitor(monitorId, app, metrics);
|
||||
List<AlertDefine> defaultDefines = alertDefineDao.queryAlertDefinesByAppAndMetricAndPresetTrueAndEnableTrue(app, metrics);
|
||||
defines.addAll(defaultDefines);
|
||||
return defines.stream().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<AlertDefine> getAlertDefines(Specification<AlertDefine> specification, PageRequest pageRequest) {
|
||||
return alertDefineDao.findAll(specification, pageRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AlertDefineMonitorBind> getBindAlertDefineMonitors(long alertDefineId) {
|
||||
return alertDefineBindDao.getAlertDefineBindsByAlertDefineIdEquals(alertDefineId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void export(List<Long> ids, String type, HttpServletResponse res) throws Exception {
|
||||
var imExportService = alertDefineImExportServiceMap.get(type);
|
||||
if (imExportService == null) {
|
||||
throw new IllegalArgumentException("not support export type: " + type);
|
||||
}
|
||||
var fileName = imExportService.getFileName();
|
||||
res.setHeader("content-type", "application/octet-stream;charset=UTF-8");
|
||||
res.setContentType("application/octet-stream;charset=UTF-8");
|
||||
res.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
|
||||
res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
|
||||
imExportService.exportConfig(res.getOutputStream(), ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importConfig(MultipartFile file) throws Exception {
|
||||
var fileName = file.getOriginalFilename();
|
||||
if (!StringUtils.hasText(fileName)) {
|
||||
return;
|
||||
}
|
||||
var type = "";
|
||||
if (fileName.toLowerCase().endsWith(AlertDefineJsonImExportServiceImpl.FILE_SUFFIX)) {
|
||||
type = AlertDefineJsonImExportServiceImpl.TYPE;
|
||||
}
|
||||
if (fileName.toLowerCase().endsWith(AlertDefineExcelImExportServiceImpl.FILE_SUFFIX)) {
|
||||
type = AlertDefineExcelImExportServiceImpl.TYPE;
|
||||
}
|
||||
if (fileName.toLowerCase().endsWith(AlertDefineYamlImExportServiceImpl.FILE_SUFFIX)) {
|
||||
type = AlertDefineYamlImExportServiceImpl.TYPE;
|
||||
}
|
||||
if (!alertDefineImExportServiceMap.containsKey(type)) {
|
||||
throw new RuntimeException("file " + fileName + " is not supported.");
|
||||
}
|
||||
var imExportService = alertDefineImExportServiceMap.get(type);
|
||||
imExportService.importConfig(file.getInputStream());
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* 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.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
/**
|
||||
* Configure the import and export Yaml format
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AlertDefineYamlImExportServiceImpl extends AlertDefineAbstractImExportServiceImpl {
|
||||
|
||||
public static final String TYPE = "YAML";
|
||||
public static final String FILE_SUFFIX = ".yaml";
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return fileNamePrefix() + FILE_SUFFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
List<ExportAlertDefineDTO> parseImport(InputStream is) {
|
||||
Yaml yaml = new Yaml();
|
||||
return yaml.load(is);
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeOs(List<ExportAlertDefineDTO> exportAlertDefineList, OutputStream os) {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
options.setIndent(2);
|
||||
options.setPrettyFlow(true);
|
||||
Yaml yaml = new Yaml(options);
|
||||
yaml.dump(exportAlertDefineList, new OutputStreamWriter(os, StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* 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.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDao;
|
||||
import org.apache.hertzbeat.alert.dto.AlertPriorityNum;
|
||||
import org.apache.hertzbeat.alert.dto.AlertSummary;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
|
||||
import org.apache.hertzbeat.alert.service.AlertService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.dto.AlertReport;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Realization of Alarm Information Service
|
||||
*/
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Slf4j
|
||||
public class AlertServiceImpl implements AlertService {
|
||||
|
||||
@Autowired
|
||||
private AlertDao alertDao;
|
||||
|
||||
@Autowired
|
||||
private AlarmCommonReduce alarmCommonReduce;
|
||||
|
||||
@Override
|
||||
public void addAlert(Alert alert) throws RuntimeException {
|
||||
alertDao.save(alert);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Alert> getAlerts(Specification<Alert> specification, PageRequest pageRequest) {
|
||||
return alertDao.findAll(specification, pageRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAlerts(HashSet<Long> ids) {
|
||||
alertDao.deleteAlertsByIdIn(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAlerts() {
|
||||
alertDao.deleteAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editAlertStatus(Byte status, List<Long> ids) {
|
||||
alertDao.updateAlertsStatus(status, ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertSummary getAlertsSummary() {
|
||||
AlertSummary alertSummary = new AlertSummary();
|
||||
// Statistics on the alarm information in the alarm state
|
||||
List<AlertPriorityNum> priorityNums = alertDao.findAlertPriorityNum();
|
||||
if (priorityNums != null) {
|
||||
for (AlertPriorityNum priorityNum : priorityNums) {
|
||||
switch (priorityNum.getPriority()) {
|
||||
case CommonConstants
|
||||
.ALERT_PRIORITY_CODE_WARNING -> alertSummary.setPriorityWarningNum(priorityNum.getNum());
|
||||
case CommonConstants.ALERT_PRIORITY_CODE_CRITICAL -> alertSummary.setPriorityCriticalNum(priorityNum.getNum());
|
||||
case CommonConstants.ALERT_PRIORITY_CODE_EMERGENCY -> alertSummary.setPriorityEmergencyNum(priorityNum.getNum());
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
long total = alertDao.count();
|
||||
alertSummary.setTotal(total);
|
||||
long dealNum = total - alertSummary.getPriorityCriticalNum()
|
||||
- alertSummary.getPriorityEmergencyNum() - alertSummary.getPriorityWarningNum();
|
||||
alertSummary.setDealNum(dealNum);
|
||||
try {
|
||||
if (total == 0) {
|
||||
alertSummary.setRate(100);
|
||||
} else {
|
||||
float rate = BigDecimal.valueOf(100 * (float) dealNum / total)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
.floatValue();
|
||||
alertSummary.setRate(rate);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return alertSummary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNewAlertReport(AlertReport alertReport) {
|
||||
alarmCommonReduce.reduceAndSendAlarm(buildAlertData(alertReport));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Alert> getAlerts(Specification<Alert> specification) {
|
||||
|
||||
return alertDao.findAll(specification);
|
||||
}
|
||||
|
||||
/**
|
||||
* The external alarm information is converted to Alert
|
||||
* @param alertReport alarm body
|
||||
* @return Alert entity
|
||||
*/
|
||||
private Alert buildAlertData(AlertReport alertReport){
|
||||
Map<String, String> annotations = alertReport.getAnnotations();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (alertReport.getContent() == null || alertReport.getContent().length() <= 0){
|
||||
StringBuilder finalSb = sb;
|
||||
annotations.forEach((k, v) -> {
|
||||
finalSb.append(k).append(":").append(v).append("\n");
|
||||
});
|
||||
} else {
|
||||
sb = new StringBuilder(alertReport.getContent());
|
||||
}
|
||||
LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(alertReport.getAlertTime()),
|
||||
ZoneId.systemDefault());
|
||||
return Alert.builder()
|
||||
.content("Alert Center\n" + sb)
|
||||
.priority(alertReport.getPriority().byteValue())
|
||||
.status(CommonConstants.ALERT_STATUS_CODE_PENDING)
|
||||
.tags(alertReport.getLabels())
|
||||
.target(alertReport.getAlertName())
|
||||
.triggerTimes(1)
|
||||
.firstAlarmTime(alertReport.getAlertTime())
|
||||
.lastAlarmTime(alertReport.getAlertTime())
|
||||
.gmtCreate(dateTime)
|
||||
.gmtUpdate(dateTime)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dao.AlertSilenceDao;
|
||||
import org.apache.hertzbeat.alert.service.AlertSilenceService;
|
||||
import org.apache.hertzbeat.common.cache.CacheFactory;
|
||||
import org.apache.hertzbeat.common.cache.CommonCacheService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertSilence;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* management interface service implement for alert silence
|
||||
*/
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Slf4j
|
||||
public class AlertSilenceServiceImpl implements AlertSilenceService {
|
||||
|
||||
@Autowired
|
||||
private AlertSilenceDao alertSilenceDao;
|
||||
|
||||
@Override
|
||||
public void validate(AlertSilence alertSilence, boolean isModify) throws IllegalArgumentException {
|
||||
// todo
|
||||
// if the alarm silent selection date set in periodic situations is empty, it will be deemed to be all checked.
|
||||
if (alertSilence.getType() == 1 && alertSilence.getDays() == null) {
|
||||
alertSilence.setDays(Arrays.asList((byte) 7, (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5, (byte) 6));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAlertSilence(AlertSilence alertSilence) throws RuntimeException {
|
||||
alertSilenceDao.save(alertSilence);
|
||||
clearAlertSilencesCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyAlertSilence(AlertSilence alertSilence) throws RuntimeException {
|
||||
alertSilenceDao.save(alertSilence);
|
||||
clearAlertSilencesCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertSilence getAlertSilence(long silenceId) throws RuntimeException {
|
||||
return alertSilenceDao.findById(silenceId).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAlertSilences(Set<Long> silenceIds) throws RuntimeException {
|
||||
alertSilenceDao.deleteAlertSilencesByIdIn(silenceIds);
|
||||
clearAlertSilencesCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<AlertSilence> getAlertSilences(Specification<AlertSilence> specification, PageRequest pageRequest) {
|
||||
return alertSilenceDao.findAll(specification, pageRequest);
|
||||
}
|
||||
|
||||
private void clearAlertSilencesCache() {
|
||||
CommonCacheService<String, Object> silenceCache = CacheFactory.getAlertSilenceCache();
|
||||
silenceCache.remove(CommonConstants.CACHE_ALERT_SILENCE);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Alarm template keyword matching replacement engine tool
|
||||
*/
|
||||
@Slf4j
|
||||
public final class AlertTemplateUtil {
|
||||
|
||||
private AlertTemplateUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the variable ${key}
|
||||
* eg: Alert, the instance: ${instance} metrics: ${metrics} is over flow.
|
||||
*/
|
||||
private static final Pattern PATTERN = Pattern.compile("\\$\\{(\\w+)\\}");
|
||||
|
||||
public static String render(String template, Map<String, Object> replaceData) {
|
||||
if (template == null) {
|
||||
return null;
|
||||
}
|
||||
if (replaceData == null) {
|
||||
log.warn("The replaceData is null.");
|
||||
return template;
|
||||
}
|
||||
try {
|
||||
Matcher matcher = PATTERN.matcher(template);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while (matcher.find()) {
|
||||
Object objectValue = replaceData.getOrDefault(matcher.group(1), "NullValue");
|
||||
String value = objectValue.toString();
|
||||
matcher.appendReplacement(builder, Matcher.quoteReplacement(value));
|
||||
}
|
||||
matcher.appendTail(builder);
|
||||
return builder.toString();
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
return template;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
# 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.
|
||||
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.apache.hertzbeat.alert.config.AlerterAutoConfiguration
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Test case for {@link AlerterWorkerPool}
|
||||
*/
|
||||
class AlerterWorkerPoolTest {
|
||||
|
||||
private static final int NUMBER_OF_THREADS = 10;
|
||||
private AlerterWorkerPool pool;
|
||||
private AtomicInteger counter;
|
||||
private CountDownLatch latch;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
pool = new AlerterWorkerPool();
|
||||
counter = new AtomicInteger();
|
||||
latch = new CountDownLatch(NUMBER_OF_THREADS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeJob() throws InterruptedException {
|
||||
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
|
||||
pool.executeJob(() -> {
|
||||
counter.incrementAndGet();
|
||||
latch.countDown();
|
||||
});
|
||||
}
|
||||
latch.await();
|
||||
|
||||
assertEquals(NUMBER_OF_THREADS, counter.get());
|
||||
}
|
||||
}
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.alert.service.impl.AlertDefineServiceImpl;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefineMonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
/**
|
||||
* Test case for {@link AlertDefineController}
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AlertDefineControllerTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private AlertDefine alertDefine;
|
||||
|
||||
private List<AlertDefineMonitorBind> alertDefineMonitorBinds;
|
||||
|
||||
@Mock
|
||||
private AlertDefineServiceImpl alertDefineService;
|
||||
|
||||
@InjectMocks
|
||||
private AlertDefineController alertDefineController;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// standaloneSetup: Standalone setup, not integrated with a web environment for testing
|
||||
this.mockMvc = MockMvcBuilders.standaloneSetup(alertDefineController).build();
|
||||
|
||||
this.alertDefine = AlertDefine.builder()
|
||||
.id(1L)
|
||||
.app("app")
|
||||
.metric("test")
|
||||
.field("test")
|
||||
.preset(false)
|
||||
.expr("1 > 0")
|
||||
.priority((byte) 1)
|
||||
.times(1)
|
||||
.template("template")
|
||||
.creator("tom")
|
||||
.modifier("tom")
|
||||
.build();
|
||||
|
||||
this.alertDefineMonitorBinds = Collections.singletonList(
|
||||
AlertDefineMonitorBind.builder()
|
||||
.id(1L)
|
||||
.alertDefineId(this.alertDefine.getId())
|
||||
.monitorId(1L)
|
||||
.monitor(
|
||||
Monitor.builder()
|
||||
.id(1L)
|
||||
.app("app")
|
||||
.host("localhost")
|
||||
.name("monitor")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addNewAlertDefine() throws Exception {
|
||||
// Simulate the client sending a request to the server
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/api/alert/define")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(this.alertDefine)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void modifyAlertDefine() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.put("/api/alert/define")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(this.alertDefine)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAlertDefine() throws Exception {
|
||||
// Simulate returning data from getAlertDefine
|
||||
Mockito.when(alertDefineService.getAlertDefine(this.alertDefine.getId()))
|
||||
.thenReturn(this.alertDefine);
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/api/alert/define/" + this.alertDefine.getId())
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.data.id").value(alertDefine.getId()))
|
||||
.andExpect(jsonPath("$.data.app").value(alertDefine.getApp()))
|
||||
.andExpect(jsonPath("$.data.metric").value(alertDefine.getMetric()))
|
||||
.andExpect(jsonPath("$.data.field").value(alertDefine.getField()))
|
||||
.andExpect(jsonPath("$.data.expr").value(alertDefine.getExpr()))
|
||||
.andExpect(jsonPath("$.data.template").value(alertDefine.getTemplate()))
|
||||
.andExpect(jsonPath("$.data.gmtCreate").value(alertDefine.getGmtCreate()))
|
||||
.andExpect(jsonPath("$.data.gmtUpdate").value(alertDefine.getGmtUpdate()))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteAlertDefine() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.delete("/api/alert/define/" + this.alertDefine.getId())
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(this.alertDefine)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyAlertDefineMonitorsBind() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.post("/api/alert/define/" + this.alertDefine.getId() + "/monitors")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(this.alertDefineMonitorBinds)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAlertDefineMonitorsBind() throws Exception {
|
||||
Mockito.when(alertDefineService.getBindAlertDefineMonitors(this.alertDefine.getId()))
|
||||
.thenReturn(this.alertDefineMonitorBinds);
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/api/alert/define/" + this.alertDefine.getId() + "/monitors")
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.data[0].id").value(alertDefineMonitorBinds.get(0).getId()))
|
||||
.andExpect(jsonPath("$.data[0].monitor.id").value(alertDefineMonitorBinds.get(0).getMonitor().getId()))
|
||||
.andReturn();
|
||||
}
|
||||
}
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
/**
|
||||
* Test case for {@link AlertDefinesController}
|
||||
* Test whether the data mocked at the mock is correct, and test whether the format of the returned data is correct
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AlertDefinesControllerTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@InjectMocks
|
||||
private AlertDefinesController alertDefinesController;
|
||||
|
||||
@Mock
|
||||
AlertDefineService alertDefineService;
|
||||
|
||||
// Parameters to avoid default values interference, default values have been replaced
|
||||
List<Long> ids = Stream.of(6565463543L, 6565463544L).collect(Collectors.toList());
|
||||
Byte priority = Byte.parseByte("1");
|
||||
String sort = "gmtCreate";
|
||||
String order = "asc";
|
||||
Integer pageIndex = 1;
|
||||
Integer pageSize = 7;
|
||||
|
||||
// Parameter collection
|
||||
Map<String, Object> content = new HashMap<>();
|
||||
|
||||
// Object for mock
|
||||
PageRequest pageRequest;
|
||||
|
||||
// Since the specification is used in dynamic proxy, it cannot be mocked
|
||||
// Missing debugging parameters are ids, priority
|
||||
// The missing part has been manually output for testing
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.mockMvc = MockMvcBuilders.standaloneSetup(alertDefinesController).build();
|
||||
content.put("ids", ids);
|
||||
content.put("priority", priority);
|
||||
content.put("sort", sort);
|
||||
content.put("order", order);
|
||||
content.put("pageIndex", pageIndex);
|
||||
content.put("pageSize", pageSize);
|
||||
Sort sortExp = Sort.by(new Sort.Order(Sort.Direction.fromString(content.get("order").toString()), content.get("sort").toString()));
|
||||
pageRequest = PageRequest.of((Integer) content.get("pageIndex"), (Integer) content.get("pageSize"), sortExp);
|
||||
}
|
||||
|
||||
// @Test
|
||||
// todo: fix this test
|
||||
void getAlertDefines() throws Exception {
|
||||
|
||||
// Test the correctness of the mock
|
||||
// Although objects cannot be mocked, stubs can be stored using class files
|
||||
// Mockito.when(alertDefineService.getAlertDefines(Mockito.any(Specification.class), Mockito.argThat(new ArgumentMatcher<PageRequest>() {
|
||||
// @Override
|
||||
// public boolean matches(PageRequest pageRequestMidden) {
|
||||
// // There are three methods in the source code that need to be compared, namely getPageNumber(), getPageSize(), getSort()
|
||||
// if(pageRequestMidden.getPageSize() == pageRequest.getPageSize() &&
|
||||
// pageRequestMidden.getPageNumber() == pageRequest.getPageNumber() &&
|
||||
// pageRequestMidden.getSort().equals(pageRequest.getSort())) {
|
||||
// return true;
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
// }))).thenReturn(new PageImpl<AlertDefine>(new ArrayList<AlertDefine>()));
|
||||
AlertDefine define = AlertDefine.builder().id(9L).app("linux").metric("disk").field("usage").expr("x").times(1).tags(new LinkedList<>()).build();
|
||||
Mockito.when(alertDefineService.getAlertDefines(Mockito.any(), Mockito.any())).thenReturn(new PageImpl<>(Collections.singletonList(define)));
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.get(
|
||||
"/api/alert/defines")
|
||||
.param("ids", ids.toString().substring(1, ids.toString().length() - 1))
|
||||
.param("priority", priority.toString())
|
||||
.param("sort", sort)
|
||||
.param("order", order)
|
||||
.param("pageIndex", pageIndex.toString())
|
||||
.param("pageSize", pageSize.toString()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.data.content").value(new ArrayList<>()))
|
||||
.andExpect(jsonPath("$.data.pageable").value("INSTANCE"))
|
||||
.andExpect(jsonPath("$.data.totalPages").value(1))
|
||||
.andExpect(jsonPath("$.data.totalElements").value(0))
|
||||
.andExpect(jsonPath("$.data.last").value(true))
|
||||
.andExpect(jsonPath("$.data.number").value(0))
|
||||
.andExpect(jsonPath("$.data.size").value(0))
|
||||
.andExpect(jsonPath("$.data.first").value(true))
|
||||
.andExpect(jsonPath("$.data.numberOfElements").value(0))
|
||||
.andExpect(jsonPath("$.data.empty").value(true))
|
||||
.andExpect(jsonPath("$.data.sort.empty").value(true))
|
||||
.andExpect(jsonPath("$.data.sort.sorted").value(false))
|
||||
.andExpect(jsonPath("$.data.sort.unsorted").value(true))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteAlertDefines() throws Exception {
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.delete("/api/alert/defines")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(ids)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
}
|
||||
-124
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import org.apache.hertzbeat.alert.dto.GeneralCloudAlertReport;
|
||||
import org.apache.hertzbeat.alert.dto.TenCloudAlertReport;
|
||||
import org.apache.hertzbeat.alert.service.AlertService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
/**
|
||||
* unit test for {@link AlertReportController }
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AlertReportControllerTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Mock
|
||||
private AlertService alertService;
|
||||
|
||||
@InjectMocks
|
||||
private AlertReportController alertReportController;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.mockMvc = MockMvcBuilders.standaloneSetup(alertReportController).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void addNewAlertReportTencent() throws Exception {
|
||||
TenCloudAlertReport.Dimensions dimensions = new TenCloudAlertReport.Dimensions();
|
||||
dimensions.setUnInstanceId("3333");
|
||||
|
||||
TenCloudAlertReport.AlarmObjInfo alarmObjInfo = new TenCloudAlertReport.AlarmObjInfo();
|
||||
alarmObjInfo.setRegion("Guangzhou");
|
||||
alarmObjInfo.setNamespace("Guangzhou1");
|
||||
alarmObjInfo.setAppId("1111");
|
||||
alarmObjInfo.setUin("2222");
|
||||
alarmObjInfo.setDimensions(dimensions);
|
||||
|
||||
TenCloudAlertReport.Conditions conditions = new TenCloudAlertReport.Conditions();
|
||||
conditions.setMetricName("xx");
|
||||
conditions.setMetricShowName("xxx");
|
||||
conditions.setCalcType("a");
|
||||
conditions.setCalcValue("aa");
|
||||
conditions.setCalcUnit("aaa");
|
||||
conditions.setCurrentValue("b");
|
||||
conditions.setCalcUnit("bb");
|
||||
conditions.setProductName("guangzhou");
|
||||
conditions.setProductShowName("Guangzhou1");
|
||||
conditions.setEventName("CVS");
|
||||
conditions.setEventShowName("Core error");
|
||||
|
||||
TenCloudAlertReport.AlarmPolicyInfo alarmPolicyInfo = new TenCloudAlertReport.AlarmPolicyInfo();
|
||||
alarmPolicyInfo.setPolicyTypeCname("x");
|
||||
alarmPolicyInfo.setPolicyName("Test1");
|
||||
alarmPolicyInfo.setConditions(conditions);
|
||||
|
||||
TenCloudAlertReport report = TenCloudAlertReport.builder()
|
||||
.sessionId("123")
|
||||
.alarmStatus("1")
|
||||
.alarmType("event")
|
||||
.durationTime(2)
|
||||
.firstOccurTime("2023-08-14 11:11:11")
|
||||
.alarmObjInfo(alarmObjInfo)
|
||||
.alarmPolicyInfo(alarmPolicyInfo)
|
||||
.build();
|
||||
mockMvc.perform(
|
||||
MockMvcRequestBuilders
|
||||
.post("/api/alerts/report/tencloud")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(report))
|
||||
)
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(content().json("{\"data\":null,\"msg\":\"Add report success\",\"code\":0}"))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void addNewAlertReport() throws Exception {
|
||||
GeneralCloudAlertReport generalCloudAlertReport = new GeneralCloudAlertReport();
|
||||
generalCloudAlertReport.setAlertDateTime("2023-02-22T07:27:15.404000000Z");
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.post("/api/alerts/report")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(generalCloudAlertReport))
|
||||
)
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(content().json("{\"data\":null,\"msg\":\"Add report success\",\"code\":0}"))
|
||||
.andReturn();
|
||||
}
|
||||
}
|
||||
-161
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.LongStream;
|
||||
import org.apache.hertzbeat.alert.dto.AlertSummary;
|
||||
import org.apache.hertzbeat.alert.service.AlertService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
/**
|
||||
* Test case for {@link AlertsController}
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AlertsControllerTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@InjectMocks
|
||||
private AlertsController alertsController;
|
||||
|
||||
@Mock
|
||||
private AlertService alertService;
|
||||
|
||||
private List<Long> ids;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.mockMvc = MockMvcBuilders.standaloneSetup(alertsController).build();
|
||||
ids = LongStream.rangeClosed(1, 10).boxed().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// todo: fix this test
|
||||
void getAlerts() throws Exception {
|
||||
String sortField = "id";
|
||||
String orderType = "asc";
|
||||
int pageIndex = 0;
|
||||
int pageSize = 10;
|
||||
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, Sort.by(new Sort.Order(Sort.Direction.fromString(orderType), sortField)));
|
||||
Page<Alert> alertPage = new PageImpl<>(Collections.singletonList(Alert.builder().build()));
|
||||
Mockito.when(
|
||||
alertService.getAlerts(
|
||||
Mockito.any(Specification.class)
|
||||
, Mockito.argThat(
|
||||
argument ->
|
||||
argument.getPageNumber() == pageRequest.getPageNumber()
|
||||
&& argument.getPageSize() == pageRequest.getPageSize()
|
||||
&& argument.getSort().equals(pageRequest.getSort())
|
||||
)
|
||||
)
|
||||
)
|
||||
.thenReturn(alertPage);
|
||||
|
||||
mockMvc.perform(
|
||||
MockMvcRequestBuilders
|
||||
.get("/api/alerts")
|
||||
.param("ids", ids.stream().map(String::valueOf).collect(Collectors.joining(",")))
|
||||
.param("monitorId", "1")
|
||||
.param("priority", "1")
|
||||
.param("status", "1")
|
||||
.param("content", "test")
|
||||
.param("sort", sortField)
|
||||
.param("order", orderType)
|
||||
.param("pageIndex", String.valueOf(pageIndex))
|
||||
.param("pageSize", String.valueOf(pageSize))
|
||||
)
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.data.content.length()").value(1))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteAlerts() throws Exception {
|
||||
mockMvc.perform(
|
||||
MockMvcRequestBuilders
|
||||
.delete("/api/alerts")
|
||||
.param("ids", ids.stream().map(String::valueOf).collect(Collectors.joining(",")))
|
||||
)
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(content().json("{\"data\":null,\"msg\":null,\"code\":0}"))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void clearAllAlerts() throws Exception {
|
||||
mockMvc.perform(
|
||||
MockMvcRequestBuilders
|
||||
.delete("/api/alerts/clear")
|
||||
)
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(content().json("{\"data\":null,\"msg\":null,\"code\":0}"))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyAlertDefinesStatus() throws Exception {
|
||||
mockMvc.perform(
|
||||
MockMvcRequestBuilders
|
||||
.put("/api/alerts/status/1")
|
||||
.param("ids", ids.stream().map(String::valueOf).collect(Collectors.joining(",")))
|
||||
)
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(content().json("{\"data\":null,\"msg\":null,\"code\":0}"))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAlertsSummary() throws Exception {
|
||||
Mockito.when(alertService.getAlertsSummary()).thenReturn(new AlertSummary());
|
||||
|
||||
mockMvc.perform(
|
||||
MockMvcRequestBuilders
|
||||
.get("/api/alerts/summary")
|
||||
)
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(content().json("{\"data\":{\"total\":0,\"dealNum\":0,\"rate\":0.0,\"priorityWarningNum\":0,\"priorityCriticalNum\":0,\"priorityEmergencyNum\":0},\"msg\":null,\"code\":0}"))
|
||||
.andReturn();
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
/*
|
||||
* 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.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.anySet;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.when;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDefineBindDao;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDefineDao;
|
||||
import org.apache.hertzbeat.alert.service.impl.AlertDefineServiceImpl;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefineMonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
* Test case for {@link AlertDefineService}
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AlertDefineServiceTest {
|
||||
|
||||
private AlertDefine alertDefine;
|
||||
|
||||
private List<AlertDefineMonitorBind> alertDefineMonitorBinds;
|
||||
|
||||
@Mock
|
||||
private AlertDefineDao alertDefineDao;
|
||||
|
||||
@Mock
|
||||
private AlertDefineBindDao alertDefineBindDao;
|
||||
|
||||
@Mock
|
||||
private List<AlertDefineImExportService> alertDefineImExportServiceList;
|
||||
|
||||
@InjectMocks
|
||||
private AlertDefineServiceImpl alertDefineService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ReflectionTestUtils.setField(this.alertDefineService, "alertDefineDao", alertDefineDao);
|
||||
ReflectionTestUtils.setField(this.alertDefineService, "alertDefineBindDao", alertDefineBindDao);
|
||||
|
||||
this.alertDefine = AlertDefine.builder()
|
||||
.id(1L)
|
||||
.app("app")
|
||||
.metric("test")
|
||||
.field("test")
|
||||
.preset(false)
|
||||
.expr("1 > 0")
|
||||
.priority((byte) 1)
|
||||
.times(1)
|
||||
.template("template")
|
||||
.creator("tom")
|
||||
.modifier("tom")
|
||||
.build();
|
||||
|
||||
this.alertDefineMonitorBinds = Collections.singletonList(
|
||||
AlertDefineMonitorBind.builder()
|
||||
.id(1L)
|
||||
.alertDefineId(this.alertDefine.getId())
|
||||
.monitorId(1L)
|
||||
.monitor(
|
||||
Monitor.builder()
|
||||
.id(1L)
|
||||
.app("app")
|
||||
.host("localhost")
|
||||
.name("monitor")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void validate() {
|
||||
assertDoesNotThrow(() -> alertDefineService.validate(alertDefine, true));
|
||||
assertDoesNotThrow(() -> alertDefineService.validate(alertDefine, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAlertDefine() {
|
||||
assertDoesNotThrow(() -> alertDefineService.addAlertDefine(alertDefine));
|
||||
when(alertDefineDao.save(alertDefine)).thenThrow(new RuntimeException());
|
||||
assertThrows(RuntimeException.class, () -> alertDefineService.addAlertDefine(alertDefine));
|
||||
}
|
||||
|
||||
@Test
|
||||
void modifyAlertDefine() {
|
||||
AlertDefine alertDefine = AlertDefine.builder().id(1L).build();
|
||||
when(alertDefineDao.save(alertDefine)).thenReturn(alertDefine);
|
||||
assertDoesNotThrow(() -> alertDefineService.modifyAlertDefine(alertDefine));
|
||||
reset();
|
||||
when(alertDefineDao.save(alertDefine)).thenThrow(new RuntimeException());
|
||||
assertThrows(RuntimeException.class, () -> alertDefineService.modifyAlertDefine(alertDefine));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteAlertDefine() {
|
||||
long id = 1L;
|
||||
doNothing().doThrow(new RuntimeException()).when(alertDefineDao).deleteById(id);
|
||||
assertDoesNotThrow(() -> alertDefineService.deleteAlertDefine(id));
|
||||
assertThrows(RuntimeException.class, () -> alertDefineService.deleteAlertDefine(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAlertDefine() {
|
||||
long id = 1L;
|
||||
AlertDefine alertDefine = AlertDefine.builder().id(id).build();
|
||||
when(alertDefineDao.findById(id)).thenReturn(Optional.of(alertDefine));
|
||||
assertDoesNotThrow(() -> alertDefineService.getAlertDefine(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteAlertDefines() {
|
||||
doNothing().when(alertDefineDao).deleteAlertDefinesByIdIn(anySet());
|
||||
assertDoesNotThrow(() -> alertDefineService.deleteAlertDefines(new HashSet<>(1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMonitorBindAlertDefines() {
|
||||
Specification<AlertDefine> specification = mock(Specification.class);
|
||||
when(alertDefineDao.findAll(specification, PageRequest.of(1, 1))).thenReturn(Page.empty());
|
||||
assertNotNull(alertDefineService.getMonitorBindAlertDefines(specification, PageRequest.of(1, 1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyBindAlertDefineMonitors() {
|
||||
long id = 1L;
|
||||
doNothing().when(alertDefineBindDao).deleteAlertDefineBindsByAlertDefineIdEquals(id);
|
||||
when(alertDefineBindDao.saveAll(alertDefineMonitorBinds)).thenReturn(alertDefineMonitorBinds);
|
||||
assertDoesNotThrow(() -> alertDefineService.applyBindAlertDefineMonitors(id, alertDefineMonitorBinds));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetMonitorBindAlertDefines() {
|
||||
List<AlertDefine> alertDefineList = new ArrayList<>();
|
||||
alertDefineList.add(this.alertDefine);
|
||||
when(alertDefineDao.queryAlertDefinesByMonitor(1L, "app", "test")).thenReturn(alertDefineList);
|
||||
when(alertDefineDao.queryAlertDefinesByAppAndMetricAndPresetTrueAndEnableTrue("app", "test")).thenReturn(alertDefineList);
|
||||
assertNotNull(alertDefineService.getMonitorBindAlertDefines(1L, "app", "test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAlertDefines() {
|
||||
Specification<AlertDefine> specification = mock(Specification.class);
|
||||
when(alertDefineDao.findAll(specification, PageRequest.of(1, 1))).thenReturn(Page.empty());
|
||||
assertNotNull(alertDefineService.getAlertDefines(specification, PageRequest.of(1, 1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBindAlertDefineMonitors() {
|
||||
long id = 1L;
|
||||
when(alertDefineBindDao.getAlertDefineBindsByAlertDefineIdEquals(id)).thenReturn(alertDefineMonitorBinds);
|
||||
assertDoesNotThrow(() -> alertDefineService.getBindAlertDefineMonitors(id));
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* 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 org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Test case for {@link AlertService}
|
||||
*/
|
||||
class AlertServiceTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void addAlert() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAlerts() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteAlerts() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void clearAlerts() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void editAlertStatus() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAlertsSummary() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void addNewAlertReport() {
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Test case for {@link AlertTemplateUtil}
|
||||
*/
|
||||
class AlertTemplateUtilTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void render() {
|
||||
// test null template case
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
String template = null;
|
||||
assertNull(AlertTemplateUtil.render(null, param));
|
||||
// test null map case
|
||||
template = "${key} for testing";
|
||||
assertEquals(AlertTemplateUtil.render(template, null), template);
|
||||
// test null template and null map case
|
||||
assertNull(AlertTemplateUtil.render(null, null));
|
||||
// test illegal template case
|
||||
template = "";
|
||||
param.put("key", "Just");
|
||||
assertEquals(AlertTemplateUtil.render(template, param), template);
|
||||
template = "key for testing!";
|
||||
assertEquals(AlertTemplateUtil.render(template, param), template);
|
||||
// test empty map case
|
||||
param.clear();
|
||||
template = "${key} for testing";
|
||||
assertEquals(AlertTemplateUtil.render(template, param), "NullValue for testing");
|
||||
// test illegal template and empty map case
|
||||
param.clear();
|
||||
template = "key for testing";
|
||||
assertEquals(AlertTemplateUtil.render(template, param), template);
|
||||
// test one param
|
||||
param.put("key", "Just");
|
||||
template = "${key} for testing";
|
||||
assertEquals(AlertTemplateUtil.render(template, param), "Just for testing");
|
||||
// test two param
|
||||
param.put("key1", "Just");
|
||||
param.put("key2", "testing");
|
||||
template = "${key1} for ${key2}";
|
||||
assertEquals(AlertTemplateUtil.render(template, param), "Just for testing");
|
||||
// test all param
|
||||
param.put("key1", "Just");
|
||||
param.put("key2", "for");
|
||||
param.put("key3", "testing");
|
||||
template = "${key1} ${key2} ${key3}";
|
||||
assertEquals(AlertTemplateUtil.render(template, param), "Just for testing");
|
||||
}
|
||||
|
||||
@Test
|
||||
void renderSpecialCharacters() {
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
param.put("valueWithDollar", "$100");
|
||||
param.put("valueWithBackslash", "C:\\Users");
|
||||
|
||||
String template = "The price is ${valueWithDollar} and the path is ${valueWithBackslash}";
|
||||
|
||||
// Expected to handle the dollar sign and backslash correctly without throwing an exception
|
||||
String expectedResult = "The price is $100 and the path is C:\\Users";
|
||||
assertEquals(expectedResult, AlertTemplateUtil.render(template, param));
|
||||
}
|
||||
}
|
||||
@@ -1,464 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hertzbeat</artifactId>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hertzbeat-collector</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
|
||||
<maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<!-- common -->
|
||||
<dependency>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat-common</artifactId>
|
||||
</dependency>
|
||||
<!-- remoting -->
|
||||
<dependency>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat-remoting</artifactId>
|
||||
</dependency>
|
||||
<!-- run sql script-->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
</dependency>
|
||||
<!-- kafka -->
|
||||
<dependency>
|
||||
<groupId>org.apache.kafka</groupId>
|
||||
<artifactId>kafka-clients</artifactId>
|
||||
</dependency>
|
||||
<!-- http -->
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<!--network-->
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</dependency>
|
||||
<!--json path parser-->
|
||||
<dependency>
|
||||
<groupId>com.jayway.jsonpath</groupId>
|
||||
<artifactId>json-path</artifactId>
|
||||
</dependency>
|
||||
<!-- lru hashmap -->
|
||||
<dependency>
|
||||
<groupId>com.googlecode.concurrentlinkedhashmap</groupId>
|
||||
<artifactId>concurrentlinkedhashmap-lru</artifactId>
|
||||
<version>1.4.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
<!--collect-->
|
||||
<!-- mysql -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- clickhouse -->
|
||||
<dependency>
|
||||
<groupId>com.clickhouse</groupId>
|
||||
<artifactId>clickhouse-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- dm -->
|
||||
<dependency>
|
||||
<groupId>com.dameng</groupId>
|
||||
<artifactId>DmJdbcDriver18</artifactId>
|
||||
</dependency>
|
||||
<!-- postgresql -->
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<!-- linux ssh -->
|
||||
<dependency>
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-core</artifactId>
|
||||
<version>2.8.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.i2p.crypto</groupId>
|
||||
<artifactId>eddsa</artifactId>
|
||||
<version>0.3.0</version>
|
||||
</dependency>
|
||||
<!-- sql server -->
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
</dependency>
|
||||
<!-- oracle -->
|
||||
<dependency>
|
||||
<groupId>com.oracle.database.jdbc</groupId>
|
||||
<artifactId>ojdbc8</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.oracle.database.nls</groupId>
|
||||
<artifactId>orai18n</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!--redis-->
|
||||
<dependency>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
</dependency>
|
||||
<!-- mongodb -->
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongodb-driver-sync</artifactId>
|
||||
</dependency>
|
||||
<!--snmp-->
|
||||
<dependency>
|
||||
<groupId>org.snmp4j</groupId>
|
||||
<artifactId>snmp4j</artifactId>
|
||||
<version>3.6.7</version>
|
||||
</dependency>
|
||||
|
||||
<!-- rocketmq -->
|
||||
<dependency>
|
||||
<groupId>org.apache.rocketmq</groupId>
|
||||
<artifactId>rocketmq-tools</artifactId>
|
||||
<version>4.9.4</version>
|
||||
</dependency>
|
||||
|
||||
<!--dns-->
|
||||
<dependency>
|
||||
<groupId>dnsjava</groupId>
|
||||
<artifactId>dnsjava</artifactId>
|
||||
<version>3.5.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- consul -->
|
||||
<dependency>
|
||||
<groupId>com.ecwid.consul</groupId>
|
||||
<artifactId>consul-api</artifactId>
|
||||
<version>1.4.5</version>
|
||||
</dependency>
|
||||
<!-- nacos -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
<!-- nebula graph client -->
|
||||
<dependency>
|
||||
<groupId>com.vesoft</groupId>
|
||||
<artifactId>client</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>apache-hertzbeat-collector-${hzb.version}</finalName>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>inner</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>${maven-jar-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>cluster</id>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>*.yml</include>
|
||||
<include>*.properties</include>
|
||||
<include>*.xml</include>
|
||||
<include>banner.txt</include>
|
||||
<include>META-INF/**</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>${maven-jar-plugin.version}</version>
|
||||
<configuration>
|
||||
<classesDirectory>target/classes/</classesDirectory>
|
||||
<archive>
|
||||
<!--Generated JAR does not include Maven descriptor-related files-->
|
||||
<addMavenDescriptor>false</addMavenDescriptor>
|
||||
<manifest>
|
||||
<!--Project startup class-->
|
||||
<mainClass>org.apache.hertzbeat.collector.Collector</mainClass>
|
||||
<useUniqueVersions>false</useUniqueVersions>
|
||||
<!--Third-party JARs are added to the classpath using maven-dependency-plugin-->
|
||||
<addClasspath>true</addClasspath>
|
||||
<!--Location of external dependency JARs-->
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<Class-Path>. config</Class-Path>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>${maven-assembly-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>without-jdk</id>
|
||||
<!--Bound Maven operation-->
|
||||
<phase>package</phase>
|
||||
<!--Run once-->
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>../script/assembly/collector/assembly.xml</descriptor>
|
||||
</descriptors>
|
||||
<outputDirectory>../dist</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>runtime</id>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>*.yml</include>
|
||||
<include>*.properties</include>
|
||||
<include>*.xml</include>
|
||||
<include>banner.txt</include>
|
||||
<include>META-INF/**</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>${maven-jar-plugin.version}</version>
|
||||
<configuration>
|
||||
<classesDirectory>target/classes/</classesDirectory>
|
||||
<archive>
|
||||
<!--Generated JAR does not include Maven descriptor-related files-->
|
||||
<addMavenDescriptor>false</addMavenDescriptor>
|
||||
<manifest>
|
||||
<!--Project startup class-->
|
||||
<mainClass>org.apache.hertzbeat.collector.Collector</mainClass>
|
||||
<useUniqueVersions>false</useUniqueVersions>
|
||||
<!--Third-party JARs are added to the classpath using maven-dependency-plugin-->
|
||||
<addClasspath>true</addClasspath>
|
||||
<!--Location of external dependency JARs-->
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<Class-Path>. config</Class-Path>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>${maven-assembly-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>without-jdk</id>
|
||||
<!--Bound Maven operation-->
|
||||
<phase>package</phase>
|
||||
<!--Run once-->
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>../script/assembly/collector/assembly.xml</descriptor>
|
||||
</descriptors>
|
||||
<outputDirectory>../dist</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>make-macos-arm64</id>
|
||||
<!--Bound Maven operation-->
|
||||
<phase>package</phase>
|
||||
<!--Run once-->
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>../script/assembly/collector/assembly-macos-arm64.xml</descriptor>
|
||||
</descriptors>
|
||||
<outputDirectory>../dist</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>make-macos-amd64</id>
|
||||
<!--Bound Maven operation-->
|
||||
<phase>package</phase>
|
||||
<!--Run once-->
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>../script/assembly/collector/assembly-macos-amd64.xml</descriptor>
|
||||
</descriptors>
|
||||
<outputDirectory>../dist</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>make-linux-arm64</id>
|
||||
<!--Bound Maven operation-->
|
||||
<phase>package</phase>
|
||||
<!--Run once-->
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>../script/assembly/collector/assembly-linux-arm64.xml</descriptor>
|
||||
</descriptors>
|
||||
<outputDirectory>../dist</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>make-linux-amd64</id>
|
||||
<!--Bound Maven operation-->
|
||||
<phase>package</phase>
|
||||
<!--Run once-->
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>../script/assembly/collector/assembly-linux-amd64.xml</descriptor>
|
||||
</descriptors>
|
||||
<outputDirectory>../dist</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>make-windows-64</id>
|
||||
<!--Bound Maven operation-->
|
||||
<phase>package</phase>
|
||||
<!--Run once-->
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>../script/assembly/collector/assembly-windows-64.xml</descriptor>
|
||||
</descriptors>
|
||||
<outputDirectory>../dist</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
-233
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.common.cache;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* lru common resource cache for client-server connection
|
||||
*/
|
||||
@Slf4j
|
||||
public class ConnectionCommonCache<T, C extends AbstractConnection<?>> {
|
||||
|
||||
/**
|
||||
* default cache time 200s
|
||||
*/
|
||||
private static final long DEFAULT_CACHE_TIMEOUT = 200 * 1000L;
|
||||
|
||||
/**
|
||||
* default max cache num
|
||||
*/
|
||||
private static final int DEFAULT_MAX_CAPACITY = 10000;
|
||||
|
||||
/**
|
||||
* cacheTime length
|
||||
*/
|
||||
private static final int CACHE_TIME_LENGTH = 2;
|
||||
|
||||
/**
|
||||
* cache timeout map
|
||||
*/
|
||||
private Map<T, Long[]> timeoutMap;
|
||||
|
||||
/**
|
||||
* object cache
|
||||
*/
|
||||
private ConcurrentLinkedHashMap<T, C> cacheMap;
|
||||
|
||||
/**
|
||||
* the executor who clean cache when timeout
|
||||
*/
|
||||
private ThreadPoolExecutor timeoutCleanerExecutor;
|
||||
|
||||
public ConnectionCommonCache() {
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
cacheMap = new ConcurrentLinkedHashMap
|
||||
.Builder<T, C>()
|
||||
.maximumWeightedCapacity(DEFAULT_MAX_CAPACITY)
|
||||
.listener((key, value) -> {
|
||||
timeoutMap.remove(key);
|
||||
try {
|
||||
value.close();
|
||||
} catch (Exception e) {
|
||||
log.error("connection close error: {}.", e.getMessage(), e);
|
||||
}
|
||||
log.info("connection common cache discard key: {}, value: {}.", key, value);
|
||||
}).build();
|
||||
timeoutMap = new ConcurrentHashMap<>(DEFAULT_MAX_CAPACITY >> 6);
|
||||
// last-first-coverage algorithm, run the first and last thread, discard mid
|
||||
timeoutCleanerExecutor = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS,
|
||||
new ArrayBlockingQueue<>(1),
|
||||
r -> new Thread(r, "connection-cache-timeout-cleaner"),
|
||||
new ThreadPoolExecutor.DiscardOldestPolicy());
|
||||
// init monitor available detector cyc task
|
||||
ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setNameFormat("connection-cache-ava-detector-%d")
|
||||
.setDaemon(true)
|
||||
.build();
|
||||
ScheduledThreadPoolExecutor scheduledExecutor = new ScheduledThreadPoolExecutor(1, threadFactory);
|
||||
scheduledExecutor.scheduleWithFixedDelay(this::detectCacheAvailable, 2, 20, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* detect all cache available, cleanup not ava connection
|
||||
*/
|
||||
private void detectCacheAvailable() {
|
||||
try {
|
||||
cacheMap.forEach((key, value) -> {
|
||||
Long[] cacheTime = timeoutMap.get(key);
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (cacheTime == null || cacheTime.length != CACHE_TIME_LENGTH
|
||||
|| cacheTime[0] + cacheTime[1] < currentTime) {
|
||||
cacheMap.remove(key);
|
||||
timeoutMap.remove(key);
|
||||
try {
|
||||
value.close();
|
||||
} catch (Exception e) {
|
||||
log.error("connection close error: {}.", e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error("connection common cache detect cache available error: {}.", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clean timeout cache
|
||||
*/
|
||||
private void cleanTimeoutCache() {
|
||||
try {
|
||||
cacheMap.forEach((key, value) -> {
|
||||
// index 0 is startTime, 1 is timeDiff
|
||||
Long[] cacheTime = timeoutMap.get(key);
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (cacheTime == null || cacheTime.length != CACHE_TIME_LENGTH) {
|
||||
timeoutMap.put(key, new Long[]{currentTime, DEFAULT_CACHE_TIMEOUT});
|
||||
} else if (cacheTime[0] + cacheTime[1] < currentTime) {
|
||||
// timeout, remove this object cache
|
||||
log.warn("[connection common cache] clean the timeout cache, key {}", key);
|
||||
timeoutMap.remove(key);
|
||||
cacheMap.remove(key);
|
||||
try {
|
||||
value.close();
|
||||
} catch (Exception e) {
|
||||
log.error("connection close error: {}.", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
Thread.sleep(20 * 1000);
|
||||
} catch (Exception e) {
|
||||
log.error("[connection common cache] clean timeout cache error: {}.", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* add update cache
|
||||
*
|
||||
* @param key cache key
|
||||
* @param value cache value
|
||||
* @param timeDiff cache time millis
|
||||
*/
|
||||
public void addCache(T key, C value, Long timeDiff) {
|
||||
removeCache(key);
|
||||
if (timeDiff == null) {
|
||||
timeDiff = DEFAULT_CACHE_TIMEOUT;
|
||||
}
|
||||
cacheMap.put(key, value);
|
||||
timeoutMap.put(key, new Long[]{System.currentTimeMillis(), timeDiff});
|
||||
timeoutCleanerExecutor.execute(this::cleanTimeoutCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* add update cache
|
||||
*
|
||||
* @param key cache key
|
||||
* @param value cache value
|
||||
*/
|
||||
public void addCache(T key, C value) {
|
||||
addCache(key, value, DEFAULT_CACHE_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* get cache by key
|
||||
*
|
||||
* @param key cache key
|
||||
* @param refreshCache is refresh cache
|
||||
* @return cache object
|
||||
*/
|
||||
public Optional<C> getCache(T key, boolean refreshCache) {
|
||||
Long[] cacheTime = timeoutMap.get(key);
|
||||
if (cacheTime == null || cacheTime.length != CACHE_TIME_LENGTH) {
|
||||
log.info("[connection common cache] not hit the cache, key {}.", key);
|
||||
return Optional.empty();
|
||||
}
|
||||
if (cacheTime[0] + cacheTime[1] < System.currentTimeMillis()) {
|
||||
log.warn("[connection common cache] is timeout, remove it, key {}.", key);
|
||||
timeoutMap.remove(key);
|
||||
cacheMap.remove(key);
|
||||
return Optional.empty();
|
||||
}
|
||||
C value = cacheMap.compute(key, (k, v) -> {
|
||||
if (v == null) {
|
||||
log.error("[connection common cache] value is null, remove it, key {}.", key);
|
||||
timeoutMap.remove(key);
|
||||
return null;
|
||||
}
|
||||
if (refreshCache) {
|
||||
cacheTime[0] = System.currentTimeMillis();
|
||||
timeoutMap.put(key, cacheTime);
|
||||
}
|
||||
return v;
|
||||
});
|
||||
return Optional.ofNullable(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove cache by key
|
||||
*
|
||||
* @param key key
|
||||
*/
|
||||
public void removeCache(T key) {
|
||||
timeoutMap.remove(key);
|
||||
C value = cacheMap.remove(key);
|
||||
try {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
value.close();
|
||||
} catch (Exception e) {
|
||||
log.error("connection close error: {}.", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-320
@@ -1,320 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.database;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollect;
|
||||
import org.apache.hertzbeat.collector.collect.common.cache.CacheIdentifier;
|
||||
import org.apache.hertzbeat.collector.collect.common.cache.ConnectionCommonCache;
|
||||
import org.apache.hertzbeat.collector.collect.common.cache.JdbcConnect;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.collector.util.CollectUtil;
|
||||
import org.apache.hertzbeat.common.constants.CollectorConstants;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.JdbcProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.postgresql.util.PSQLException;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.jdbc.datasource.init.ScriptUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* common query for database query
|
||||
*/
|
||||
@Slf4j
|
||||
public class JdbcCommonCollect extends AbstractCollect {
|
||||
|
||||
private static final String QUERY_TYPE_ONE_ROW = "oneRow";
|
||||
private static final String QUERY_TYPE_MULTI_ROW = "multiRow";
|
||||
private static final String QUERY_TYPE_COLUMNS = "columns";
|
||||
private static final String RUN_SCRIPT = "runScript";
|
||||
|
||||
private static final String[] VULNERABLE_KEYWORDS = {"allowLoadLocalInfile", "allowLoadLocalInfileInPath", "useLocalInfile"};
|
||||
|
||||
private final ConnectionCommonCache<CacheIdentifier, JdbcConnect> connectionCommonCache;
|
||||
|
||||
public JdbcCommonCollect(){
|
||||
connectionCommonCache = new ConnectionCommonCache<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preCheck(Metrics metrics) throws IllegalArgumentException {
|
||||
if (metrics == null || metrics.getJdbc() == null) {
|
||||
throw new IllegalArgumentException("Database collect must has jdbc params");
|
||||
}
|
||||
if (StringUtils.hasText(metrics.getJdbc().getUrl())) {
|
||||
for (String keyword : VULNERABLE_KEYWORDS) {
|
||||
if (metrics.getJdbc().getUrl().contains(keyword)) {
|
||||
throw new IllegalArgumentException("Jdbc url prohibit contains vulnerable param " + keyword);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder, long monitorId, String app, Metrics metrics) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
JdbcProtocol jdbcProtocol = metrics.getJdbc();
|
||||
String databaseUrl = constructDatabaseUrl(jdbcProtocol);
|
||||
int timeout = CollectUtil.getTimeout(jdbcProtocol.getTimeout());
|
||||
Statement statement = null;
|
||||
try {
|
||||
statement = getConnection(jdbcProtocol.getUsername(),
|
||||
jdbcProtocol.getPassword(), databaseUrl, timeout);
|
||||
switch (jdbcProtocol.getQueryType()) {
|
||||
case QUERY_TYPE_ONE_ROW -> queryOneRow(statement, jdbcProtocol.getSql(), metrics.getAliasFields(), builder, startTime);
|
||||
case QUERY_TYPE_MULTI_ROW -> queryMultiRow(statement, jdbcProtocol.getSql(), metrics.getAliasFields(), builder, startTime);
|
||||
case QUERY_TYPE_COLUMNS -> queryOneRowByMatchTwoColumns(statement, jdbcProtocol.getSql(), metrics.getAliasFields(), builder, startTime);
|
||||
case RUN_SCRIPT -> {
|
||||
Connection connection = statement.getConnection();
|
||||
FileSystemResource rc = new FileSystemResource(jdbcProtocol.getSql());
|
||||
ScriptUtils.executeSqlScript(connection, rc);
|
||||
}
|
||||
default -> {
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("Not support database query type: " + jdbcProtocol.getQueryType());
|
||||
}
|
||||
}
|
||||
} catch (PSQLException psqlException) {
|
||||
// for PostgreSQL 08001
|
||||
if (CollectorConstants.POSTGRESQL_UN_REACHABLE_CODE.equals(psqlException.getSQLState())) {
|
||||
// Peer connection failed, unreachable
|
||||
builder.setCode(CollectRep.Code.UN_REACHABLE);
|
||||
} else {
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
}
|
||||
builder.setMsg("Error: " + psqlException.getMessage() + " Code: " + psqlException.getSQLState());
|
||||
} catch (SQLException sqlException) {
|
||||
log.warn("Jdbc sql error: {}, code: {}.", sqlException.getMessage(), sqlException.getErrorCode());
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("Query Error: " + sqlException.getMessage() + " Code: " + sqlException.getErrorCode());
|
||||
} catch (Exception e) {
|
||||
String errorMessage = CommonUtil.getMessageFromThrowable(e);
|
||||
log.error("Jdbc error: {}.", errorMessage, e);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("Query Error: " + errorMessage);
|
||||
} finally {
|
||||
if (statement != null) {
|
||||
try {
|
||||
statement.close();
|
||||
} catch (Exception e) {
|
||||
log.error("Jdbc close statement error: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportProtocol() {
|
||||
return DispatchConstants.PROTOCOL_JDBC;
|
||||
}
|
||||
|
||||
|
||||
private Statement getConnection(String username, String password, String url, Integer timeout) throws Exception {
|
||||
CacheIdentifier identifier = CacheIdentifier.builder()
|
||||
.ip(url)
|
||||
.username(username).password(password).build();
|
||||
Optional<JdbcConnect> cacheOption = connectionCommonCache.getCache(identifier, true);
|
||||
Statement statement = null;
|
||||
if (cacheOption.isPresent()) {
|
||||
JdbcConnect jdbcConnect = cacheOption.get();
|
||||
try {
|
||||
statement = jdbcConnect.getConnection().createStatement();
|
||||
// set query timeout
|
||||
int timeoutSecond = timeout / 1000;
|
||||
timeoutSecond = timeoutSecond <= 0 ? 1 : timeoutSecond;
|
||||
statement.setQueryTimeout(timeoutSecond);
|
||||
// set query max row number
|
||||
statement.setMaxRows(1000);
|
||||
} catch (Exception e) {
|
||||
log.info("The jdbc connect from cache, create statement error: {}", e.getMessage());
|
||||
try {
|
||||
if (statement != null) {
|
||||
statement.close();
|
||||
}
|
||||
jdbcConnect.close();
|
||||
} catch (Exception e2) {
|
||||
log.error(e2.getMessage());
|
||||
}
|
||||
statement = null;
|
||||
connectionCommonCache.removeCache(identifier);
|
||||
}
|
||||
}
|
||||
if (statement != null) {
|
||||
return statement;
|
||||
}
|
||||
// renew connection when failed
|
||||
Connection connection = DriverManager.getConnection(url, username, password);
|
||||
statement = connection.createStatement();
|
||||
int timeoutSecond = timeout / 1000;
|
||||
timeoutSecond = timeoutSecond <= 0 ? 1 : timeoutSecond;
|
||||
statement.setQueryTimeout(timeoutSecond);
|
||||
statement.setMaxRows(1000);
|
||||
JdbcConnect jdbcConnect = new JdbcConnect(connection);
|
||||
connectionCommonCache.addCache(identifier, jdbcConnect);
|
||||
return statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* query one row record, response metrics header and one value row
|
||||
* eg:
|
||||
* query metrics:one tow three four
|
||||
* query sql:select one, tow, three, four from book limit 1;
|
||||
* @param statement statement
|
||||
* @param sql sql
|
||||
* @param columns query metrics field list
|
||||
* @throws Exception when error happen
|
||||
*/
|
||||
private void queryOneRow(Statement statement, String sql, List<String> columns,
|
||||
CollectRep.MetricsData.Builder builder, long startTime) throws Exception {
|
||||
statement.setMaxRows(1);
|
||||
try (ResultSet resultSet = statement.executeQuery(sql)) {
|
||||
if (resultSet.next()) {
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String column : columns) {
|
||||
if (CollectorConstants.RESPONSE_TIME.equals(column)) {
|
||||
long time = System.currentTimeMillis() - startTime;
|
||||
valueRowBuilder.addColumns(String.valueOf(time));
|
||||
} else {
|
||||
String value = resultSet.getString(column);
|
||||
value = value == null ? CommonConstants.NULL_VALUE : value;
|
||||
valueRowBuilder.addColumns(value);
|
||||
}
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* query two columns to mapping one row
|
||||
* eg:
|
||||
* query metrics:one two three four
|
||||
* query sql:select key, value from book; the key is the query metrics fields
|
||||
* select key, value from book;
|
||||
* one - value1
|
||||
* two - value2
|
||||
* three - value3
|
||||
* four - value4
|
||||
* @param statement statement
|
||||
* @param sql sql
|
||||
* @param columns query metrics field list
|
||||
* @throws Exception when error happen
|
||||
*/
|
||||
private void queryOneRowByMatchTwoColumns(Statement statement, String sql, List<String> columns,
|
||||
CollectRep.MetricsData.Builder builder, long startTime) throws Exception {
|
||||
try (ResultSet resultSet = statement.executeQuery(sql)) {
|
||||
HashMap<String, String> values = new HashMap<>(columns.size());
|
||||
while (resultSet.next()) {
|
||||
if (resultSet.getString(1) != null) {
|
||||
values.put(resultSet.getString(1).toLowerCase().trim(), resultSet.getString(2));
|
||||
}
|
||||
}
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String column : columns) {
|
||||
if (CollectorConstants.RESPONSE_TIME.equals(column)) {
|
||||
long time = System.currentTimeMillis() - startTime;
|
||||
valueRowBuilder.addColumns(String.valueOf(time));
|
||||
} else {
|
||||
String value = values.get(column.toLowerCase());
|
||||
value = value == null ? CommonConstants.NULL_VALUE : value;
|
||||
valueRowBuilder.addColumns(value);
|
||||
}
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* query multi row record, response metrics header and multi value row
|
||||
* eg:
|
||||
* query metrics:one tow three four
|
||||
* query sql:select one, tow, three, four from book;
|
||||
* and return multi row record mapping with the metrics
|
||||
* @param statement statement
|
||||
* @param sql sql
|
||||
* @param columns query metrics field list
|
||||
* @throws Exception when error happen
|
||||
*/
|
||||
private void queryMultiRow(Statement statement, String sql, List<String> columns,
|
||||
CollectRep.MetricsData.Builder builder, long startTime) throws Exception {
|
||||
try (ResultSet resultSet = statement.executeQuery(sql)) {
|
||||
while (resultSet.next()) {
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String column : columns) {
|
||||
if (CollectorConstants.RESPONSE_TIME.equals(column)) {
|
||||
long time = System.currentTimeMillis() - startTime;
|
||||
valueRowBuilder.addColumns(String.valueOf(time));
|
||||
} else {
|
||||
String value = resultSet.getString(column);
|
||||
value = value == null ? CommonConstants.NULL_VALUE : value;
|
||||
valueRowBuilder.addColumns(value);
|
||||
}
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* construct jdbc url due the jdbc protocol
|
||||
* @param jdbcProtocol jdbc
|
||||
* @return URL
|
||||
*/
|
||||
private String constructDatabaseUrl(JdbcProtocol jdbcProtocol) {
|
||||
if (Objects.nonNull(jdbcProtocol.getUrl())
|
||||
&& !Objects.equals("", jdbcProtocol.getUrl())
|
||||
&& jdbcProtocol.getUrl().startsWith("jdbc")) {
|
||||
// when has config jdbc url, use it
|
||||
return jdbcProtocol.getUrl();
|
||||
}
|
||||
return switch (jdbcProtocol.getPlatform()) {
|
||||
case "mysql", "mariadb" ->
|
||||
"jdbc:mysql://" + jdbcProtocol.getHost() + ":" + jdbcProtocol.getPort()
|
||||
+ "/" + (jdbcProtocol.getDatabase() == null ? "" : jdbcProtocol.getDatabase())
|
||||
+ "?useUnicode=true&characterEncoding=utf-8&useSSL=false";
|
||||
case "postgresql" ->
|
||||
"jdbc:postgresql://" + jdbcProtocol.getHost() + ":" + jdbcProtocol.getPort()
|
||||
+ "/" + (jdbcProtocol.getDatabase() == null ? "" : jdbcProtocol.getDatabase());
|
||||
case "clickhouse" ->
|
||||
"jdbc:clickhouse://" + jdbcProtocol.getHost() + ":" + jdbcProtocol.getPort()
|
||||
+ "/" + (jdbcProtocol.getDatabase() == null ? "" : jdbcProtocol.getDatabase());
|
||||
case "sqlserver" ->
|
||||
"jdbc:sqlserver://" + jdbcProtocol.getHost() + ":" + jdbcProtocol.getPort()
|
||||
+ ";" + (jdbcProtocol.getDatabase() == null ? "" : "DatabaseName=" + jdbcProtocol.getDatabase())
|
||||
+ ";trustServerCertificate=true;";
|
||||
case "oracle" ->
|
||||
"jdbc:oracle:thin:@" + jdbcProtocol.getHost() + ":" + jdbcProtocol.getPort()
|
||||
+ "/" + (jdbcProtocol.getDatabase() == null ? "" : jdbcProtocol.getDatabase());
|
||||
case "dm" ->
|
||||
"jdbc:dm://" + jdbcProtocol.getHost() + ":" + jdbcProtocol.getPort();
|
||||
default -> throw new IllegalArgumentException("Not support database platform: " + jdbcProtocol.getPlatform());
|
||||
};
|
||||
}
|
||||
}
|
||||
-153
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.ftp;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollect;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.FtpProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* ftp protocol collection implementation
|
||||
*/
|
||||
@Slf4j
|
||||
public class FtpCollectImpl extends AbstractCollect {
|
||||
|
||||
private static final String ANONYMOUS = "anonymous";
|
||||
private static final String PASSWORD = "password";
|
||||
|
||||
/**
|
||||
* preCheck params
|
||||
*/
|
||||
public void preCheck(Metrics metrics) throws IllegalArgumentException{
|
||||
if (metrics == null || metrics.getFtp() == null) {
|
||||
throw new IllegalArgumentException("Ftp collect must has ftp params.");
|
||||
}
|
||||
FtpProtocol ftpProtocol = metrics.getFtp();
|
||||
Assert.hasText(ftpProtocol.getHost(), "Ftp Protocol host is required.");
|
||||
Assert.hasText(ftpProtocol.getPort(), "Ftp Protocol port is required.");
|
||||
Assert.hasText(ftpProtocol.getDirection(), "Ftp Protocol direction is required.");
|
||||
Assert.hasText(ftpProtocol.getTimeout(), "Ftp Protocol timeout is required.");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder, long monitorId, String app, Metrics metrics) {
|
||||
FTPClient ftpClient = new FTPClient();
|
||||
FtpProtocol ftpProtocol = metrics.getFtp();
|
||||
// Set timeout
|
||||
ftpClient.setControlKeepAliveReplyTimeout(Integer.parseInt(ftpProtocol.getTimeout()));
|
||||
|
||||
// Collect data to load in CollectRep.ValueRow.Builder's object
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
Map<String, String> valueMap;
|
||||
try {
|
||||
valueMap = collectValue(ftpClient, ftpProtocol);
|
||||
metrics.getAliasFields().forEach(it -> {
|
||||
if (valueMap.containsKey(it)) {
|
||||
String fieldValue = valueMap.get(it);
|
||||
valueRowBuilder.addColumns(Objects.requireNonNullElse(fieldValue, CommonConstants.NULL_VALUE));
|
||||
} else {
|
||||
valueRowBuilder.addColumns(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg(e.getMessage());
|
||||
return;
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* collect data: key-value
|
||||
* Please modify this, if you want to add some metrics.
|
||||
*/
|
||||
private Map<String, String> collectValue(FTPClient ftpClient, FtpProtocol ftpProtocol) {
|
||||
boolean isActive;
|
||||
String responseTime;
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
connect(ftpClient, ftpProtocol);
|
||||
login(ftpClient, ftpProtocol);
|
||||
// In here, we can do some extended operation without changing the architecture
|
||||
isActive = ftpClient.changeWorkingDirectory(ftpProtocol.getDirection());
|
||||
long endTime = System.currentTimeMillis();
|
||||
responseTime = String.valueOf(endTime - startTime);
|
||||
ftpClient.disconnect();
|
||||
} catch (Exception e) {
|
||||
log.info("[FTPClient] error: {}", CommonUtil.getMessageFromThrowable(e), e);
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
return new HashMap<>(8) {
|
||||
{
|
||||
put("isActive", Boolean.toString(isActive));
|
||||
put("responseTime", responseTime);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* login
|
||||
*/
|
||||
private void login(FTPClient ftpClient, FtpProtocol ftpProtocol) {
|
||||
try {
|
||||
// username: not empty, password: not empty
|
||||
if (StringUtils.hasText(ftpProtocol.getUsername()) && StringUtils.hasText(ftpProtocol.getPassword())) {
|
||||
if (!ftpClient.login(ftpProtocol.getUsername(), ftpProtocol.getPassword())) {
|
||||
throw new IllegalArgumentException("The username or password may be wrong.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
// anonymous access
|
||||
if (!ftpClient.login(ANONYMOUS, PASSWORD)) {
|
||||
throw new IllegalArgumentException("The server may not allow anonymous access, we need to username and password.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.info("[ftp login] error: {}", CommonUtil.getMessageFromThrowable(e), e);
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* connect
|
||||
*/
|
||||
private void connect(FTPClient ftpClient, FtpProtocol ftpProtocol) {
|
||||
try {
|
||||
ftpClient.connect(ftpProtocol.getHost(), Integer.parseInt(ftpProtocol.getPort()));
|
||||
} catch (Exception e) {
|
||||
log.info("[ftp connection] error: {}", CommonUtil.getMessageFromThrowable(e), e);
|
||||
throw new IllegalArgumentException("The host or port may be wrong.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportProtocol() {
|
||||
return DispatchConstants.PROTOCOL_FTP;
|
||||
}
|
||||
}
|
||||
-586
@@ -1,586 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.http;
|
||||
|
||||
import static org.apache.hertzbeat.common.constants.SignConstants.RIGHT_DASH;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.net.util.Base64;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollect;
|
||||
import org.apache.hertzbeat.collector.collect.common.http.CommonHttpClient;
|
||||
import org.apache.hertzbeat.collector.collect.http.promethus.AbstractPrometheusParse;
|
||||
import org.apache.hertzbeat.collector.collect.http.promethus.PrometheusParseCreater;
|
||||
import org.apache.hertzbeat.collector.collect.http.promethus.exporter.ExporterParser;
|
||||
import org.apache.hertzbeat.collector.collect.http.promethus.exporter.MetricFamily;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.collector.util.CollectUtil;
|
||||
import org.apache.hertzbeat.collector.util.JsonPathParser;
|
||||
import org.apache.hertzbeat.collector.util.TimeExpressionUtil;
|
||||
import org.apache.hertzbeat.common.constants.CollectorConstants;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.HttpProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.apache.hertzbeat.common.util.IpDomainUtil;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.auth.AuthScope;
|
||||
import org.apache.http.auth.UsernamePasswordCredentials;
|
||||
import org.apache.http.client.AuthCache;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.CredentialsProvider;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.client.methods.RequestBuilder;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.auth.DigestScheme;
|
||||
import org.apache.http.impl.client.BasicAuthCache;
|
||||
import org.apache.http.impl.client.BasicCredentialsProvider;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
/**
|
||||
* http https collect
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpCollectImpl extends AbstractCollect {
|
||||
private static final Map<Long, ExporterParser> EXPORTER_PARSER_TABLE = new ConcurrentHashMap<>();
|
||||
private final Set<Integer> defaultSuccessStatusCodes = Stream.of(HttpStatus.SC_OK, HttpStatus.SC_CREATED,
|
||||
HttpStatus.SC_ACCEPTED, HttpStatus.SC_MULTIPLE_CHOICES, HttpStatus.SC_MOVED_PERMANENTLY,
|
||||
HttpStatus.SC_MOVED_TEMPORARILY).collect(Collectors.toSet());
|
||||
|
||||
@Override
|
||||
public void preCheck(Metrics metrics) throws IllegalArgumentException {
|
||||
if (metrics == null || metrics.getHttp() == null) {
|
||||
throw new IllegalArgumentException("Http/Https collect must has http params");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder,
|
||||
long monitorId, String app, Metrics metrics) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
HttpProtocol httpProtocol = metrics.getHttp();
|
||||
String url = httpProtocol.getUrl();
|
||||
if (!StringUtils.hasText(url) || !url.startsWith(RIGHT_DASH)) {
|
||||
httpProtocol.setUrl(StringUtils.hasText(url) ? RIGHT_DASH + url.trim() : RIGHT_DASH);
|
||||
}
|
||||
if (CollectionUtils.isEmpty(httpProtocol.getSuccessCodes())) {
|
||||
httpProtocol.setSuccessCodes(List.of("200"));
|
||||
}
|
||||
|
||||
HttpContext httpContext = createHttpContext(metrics.getHttp());
|
||||
HttpUriRequest request = createHttpRequest(metrics.getHttp());
|
||||
try (CloseableHttpResponse response = CommonHttpClient.getHttpClient().execute(request, httpContext)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
boolean isSuccessInvoke = checkSuccessInvoke(metrics, statusCode);
|
||||
log.debug("http response status: {}", statusCode);
|
||||
if (!isSuccessInvoke) {
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("StatusCode " + statusCode);
|
||||
return;
|
||||
}
|
||||
// todo This code converts an InputStream directly to a String. For large data in Prometheus exporters,
|
||||
// this could create large objects, potentially impacting JVM memory space significantly.
|
||||
// Option 1: Parse using InputStream, but this requires significant code changes;
|
||||
// Option 2: Manually trigger garbage collection, similar to how it's done in Dubbo for large inputs.
|
||||
String resp = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
|
||||
if (StringUtils.hasText(resp)) {
|
||||
log.info("http response entity is empty, status: {}.", statusCode);
|
||||
}
|
||||
Long responseTime = System.currentTimeMillis() - startTime;
|
||||
String parseType = metrics.getHttp().getParseType();
|
||||
try {
|
||||
switch (parseType) {
|
||||
case DispatchConstants.PARSE_JSON_PATH ->
|
||||
parseResponseByJsonPath(resp, metrics.getAliasFields(), metrics.getHttp(), builder, responseTime);
|
||||
case DispatchConstants.PARSE_PROM_QL ->
|
||||
parseResponseByPromQl(resp, metrics.getAliasFields(), metrics.getHttp(), builder);
|
||||
case DispatchConstants.PARSE_PROMETHEUS ->
|
||||
parseResponseByPrometheusExporter(resp, metrics.getAliasFields(), builder);
|
||||
case DispatchConstants.PARSE_XML_PATH ->
|
||||
parseResponseByXmlPath(resp, metrics.getAliasFields(), metrics.getHttp(), builder);
|
||||
case DispatchConstants.PARSE_WEBSITE ->
|
||||
parseResponseByWebsite(resp, metrics.getAliasFields(), metrics.getHttp(), builder, responseTime);
|
||||
case DispatchConstants.PARSE_SITE_MAP ->
|
||||
parseResponseBySiteMap(resp, metrics.getAliasFields(), builder);
|
||||
default ->
|
||||
parseResponseByDefault(resp, metrics.getAliasFields(), metrics.getHttp(), builder, responseTime);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.info("parse error: {}.", e.getMessage(), e);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("parse response data error:" + e.getMessage());
|
||||
}
|
||||
} catch (ClientProtocolException e1) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e1);
|
||||
log.error(errorMsg);
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg(errorMsg);
|
||||
} catch (UnknownHostException e2) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e2);
|
||||
log.info(errorMsg);
|
||||
builder.setCode(CollectRep.Code.UN_REACHABLE);
|
||||
builder.setMsg("unknown host:" + errorMsg);
|
||||
} catch (InterruptedIOException | ConnectException | SSLException e3) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e3);
|
||||
log.info(errorMsg);
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg(errorMsg);
|
||||
} catch (IOException e4) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e4);
|
||||
log.info(errorMsg);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg(errorMsg);
|
||||
} catch (Exception e) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e);
|
||||
log.error(errorMsg, e);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg(errorMsg);
|
||||
} finally {
|
||||
if (request != null) {
|
||||
request.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportProtocol() {
|
||||
return DispatchConstants.PROTOCOL_HTTP;
|
||||
}
|
||||
|
||||
private void parseResponseByWebsite(String resp, List<String> aliasFields, HttpProtocol http,
|
||||
CollectRep.MetricsData.Builder builder, Long responseTime) {
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
int keywordNum = CollectUtil.countMatchKeyword(resp, http.getKeyword());
|
||||
for (String alias : aliasFields) {
|
||||
if (CollectorConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(responseTime.toString());
|
||||
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(Integer.toString(keywordNum));
|
||||
} else {
|
||||
valueRowBuilder.addColumns(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
|
||||
private void parseResponseBySiteMap(String resp, List<String> aliasFields,
|
||||
CollectRep.MetricsData.Builder builder) {
|
||||
List<String> siteUrls = new LinkedList<>();
|
||||
boolean isXmlFormat = true;
|
||||
try {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
Document document = db.parse(new ByteArrayInputStream(resp.getBytes(StandardCharsets.UTF_8)));
|
||||
NodeList urlList = document.getElementsByTagName("url");
|
||||
for (int i = 0; i < urlList.getLength(); i++) {
|
||||
Node urlNode = urlList.item(i);
|
||||
NodeList childNodes = urlNode.getChildNodes();
|
||||
for (int k = 0; k < childNodes.getLength(); k++) {
|
||||
Node currentNode = childNodes.item(k);
|
||||
// distinguish between text nodes and element nodes
|
||||
if (currentNode.getNodeType() == Node.ELEMENT_NODE && "loc".equals(currentNode.getNodeName())) {
|
||||
// retrieves the value of the loc node
|
||||
siteUrls.add(currentNode.getFirstChild().getNodeValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn(e.getMessage());
|
||||
isXmlFormat = false;
|
||||
}
|
||||
// if XML parsing fails, parse in TXT format
|
||||
if (!isXmlFormat) {
|
||||
try {
|
||||
String[] urls = resp.split("\n");
|
||||
// validate whether the given value is a URL
|
||||
if (IpDomainUtil.isHasSchema(urls[0])) {
|
||||
siteUrls.addAll(Arrays.asList(urls));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
// start looping through each site URL to collect its HTTP status code, response time, and exception information
|
||||
for (String siteUrl : siteUrls) {
|
||||
String errorMsg = "";
|
||||
Integer statusCode = null;
|
||||
long startTime = System.currentTimeMillis();
|
||||
try {
|
||||
HttpGet httpGet = new HttpGet(siteUrl);
|
||||
CloseableHttpResponse response = CommonHttpClient.getHttpClient().execute(httpGet);
|
||||
statusCode = response.getStatusLine().getStatusCode();
|
||||
EntityUtils.consume(response.getEntity());
|
||||
} catch (ClientProtocolException e1) {
|
||||
if (e1.getCause() != null) {
|
||||
errorMsg = e1.getCause().getMessage();
|
||||
} else {
|
||||
errorMsg = e1.getMessage();
|
||||
}
|
||||
} catch (UnknownHostException e2) {
|
||||
errorMsg = "unknown host";
|
||||
} catch (InterruptedIOException | ConnectException | SSLException e3) {
|
||||
errorMsg = "connect error: " + e3.getMessage();
|
||||
} catch (IOException e4) {
|
||||
errorMsg = "io error: " + e4.getMessage();
|
||||
} catch (Exception e) {
|
||||
errorMsg = "error: " + e.getMessage();
|
||||
}
|
||||
long responseTime = System.currentTimeMillis() - startTime;
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String alias : aliasFields) {
|
||||
if (CollectorConstants.URL.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(siteUrl);
|
||||
} else if (CollectorConstants.STATUS_CODE.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(statusCode == null
|
||||
? CommonConstants.NULL_VALUE : String.valueOf(statusCode));
|
||||
} else if (CollectorConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(String.valueOf(responseTime));
|
||||
} else if (CollectorConstants.ERROR_MSG.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(errorMsg);
|
||||
} else {
|
||||
valueRowBuilder.addColumns(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private void parseResponseByXmlPath(String resp, List<String> aliasFields, HttpProtocol http,
|
||||
CollectRep.MetricsData.Builder builder) {
|
||||
}
|
||||
|
||||
private void parseResponseByJsonPath(String resp, List<String> aliasFields, HttpProtocol http,
|
||||
CollectRep.MetricsData.Builder builder, Long responseTime) {
|
||||
List<Object> results = JsonPathParser.parseContentWithJsonPath(resp, http.getParseScript());
|
||||
int keywordNum = CollectUtil.countMatchKeyword(resp, http.getKeyword());
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
Object objectValue = results.get(i);
|
||||
// if a property is missing or empty due to target version issues, filter it. Refer to the app-elasticsearch.yml configuration under name: nodes
|
||||
if (objectValue == null) {
|
||||
continue;
|
||||
}
|
||||
if (objectValue instanceof Map) {
|
||||
Map<String, Object> stringMap = (Map<String, Object>) objectValue;
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String alias : aliasFields) {
|
||||
Object value = stringMap.get(alias);
|
||||
if (value != null) {
|
||||
valueRowBuilder.addColumns(String.valueOf(value));
|
||||
} else {
|
||||
if (alias.startsWith("$.")) {
|
||||
List<Object> subResults = JsonPathParser.parseContentWithJsonPath(resp, http.getParseScript() + alias.substring(1));
|
||||
if (subResults != null && subResults.size() > i) {
|
||||
Object resultValue = subResults.get(i);
|
||||
valueRowBuilder.addColumns(resultValue == null ? CommonConstants.NULL_VALUE : String.valueOf(resultValue));
|
||||
} else {
|
||||
valueRowBuilder.addColumns(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
} else if (CollectorConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(responseTime.toString());
|
||||
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(Integer.toString(keywordNum));
|
||||
} else {
|
||||
valueRowBuilder.addColumns(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
} else if (objectValue instanceof String stringValue) {
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String alias : aliasFields) {
|
||||
if (CollectorConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(responseTime.toString());
|
||||
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(Integer.toString(keywordNum));
|
||||
} else {
|
||||
valueRowBuilder.addColumns(stringValue);
|
||||
}
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseResponseByPromQl(String resp, List<String> aliasFields, HttpProtocol http,
|
||||
CollectRep.MetricsData.Builder builder) {
|
||||
AbstractPrometheusParse prometheusParser = PrometheusParseCreater.getPrometheusParse();
|
||||
prometheusParser.handle(resp, aliasFields, http, builder);
|
||||
}
|
||||
|
||||
private void parseResponseByPrometheusExporter(String resp, List<String> aliasFields,
|
||||
CollectRep.MetricsData.Builder builder) {
|
||||
if (!EXPORTER_PARSER_TABLE.containsKey(builder.getId())) {
|
||||
EXPORTER_PARSER_TABLE.put(builder.getId(), new ExporterParser());
|
||||
}
|
||||
ExporterParser parser = EXPORTER_PARSER_TABLE.get(builder.getId());
|
||||
Map<String, MetricFamily> metricFamilyMap = parser.textToMetric(resp);
|
||||
String metrics = builder.getMetrics();
|
||||
if (metricFamilyMap.containsKey(metrics)) {
|
||||
MetricFamily metricFamily = metricFamilyMap.get(metrics);
|
||||
for (MetricFamily.Metric metric : metricFamily.getMetricList()) {
|
||||
Map<String, String> labelMap = metric.getLabelPair()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(MetricFamily.Label::getName, MetricFamily.Label::getValue));
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String aliasField : aliasFields) {
|
||||
if ("value".equals(aliasField)) {
|
||||
if (metric.getCounter() != null) {
|
||||
valueRowBuilder.addColumns(String.valueOf(metric.getCounter().getValue()));
|
||||
} else if (metric.getGauge() != null) {
|
||||
valueRowBuilder.addColumns(String.valueOf(metric.getGauge().getValue()));
|
||||
} else if (metric.getUntyped() != null) {
|
||||
valueRowBuilder.addColumns(String.valueOf(metric.getUntyped().getValue()));
|
||||
} else if (metric.getInfo() != null) {
|
||||
valueRowBuilder.addColumns(String.valueOf(metric.getInfo().getValue()));
|
||||
} else if (metric.getSummary() != null) {
|
||||
valueRowBuilder.addColumns(String.valueOf(metric.getSummary().getValue()));
|
||||
} else if (metric.getHistogram() != null) {
|
||||
valueRowBuilder.addColumns(String.valueOf(metric.getHistogram().getValue()));
|
||||
}
|
||||
} else {
|
||||
String columnValue = labelMap.get(aliasField);
|
||||
valueRowBuilder.addColumns(columnValue == null ? CommonConstants.NULL_VALUE : columnValue);
|
||||
}
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseResponseByDefault(String resp, List<String> aliasFields, HttpProtocol http,
|
||||
CollectRep.MetricsData.Builder builder, Long responseTime) {
|
||||
JsonElement element = JsonParser.parseString(resp);
|
||||
int keywordNum = CollectUtil.countMatchKeyword(resp, http.getKeyword());
|
||||
if (element.isJsonArray()) {
|
||||
JsonArray array = element.getAsJsonArray();
|
||||
for (JsonElement jsonElement : array) {
|
||||
getValueFromJson(aliasFields, builder, responseTime, jsonElement, keywordNum);
|
||||
}
|
||||
} else {
|
||||
getValueFromJson(aliasFields, builder, responseTime, element, keywordNum);
|
||||
}
|
||||
}
|
||||
|
||||
private void getValueFromJson(List<String> aliasFields, CollectRep.MetricsData.Builder builder, Long responseTime, JsonElement element, int keywordNum) {
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String alias : aliasFields) {
|
||||
JsonElement valueElement = object.get(alias);
|
||||
if (valueElement != null) {
|
||||
String value = valueElement.getAsString();
|
||||
valueRowBuilder.addColumns(value);
|
||||
} else {
|
||||
if (CollectorConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(responseTime.toString());
|
||||
} else if (CollectorConstants.KEYWORD.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(Integer.toString(keywordNum));
|
||||
} else {
|
||||
valueRowBuilder.addColumns(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create httpContext
|
||||
*
|
||||
* @param httpProtocol http protocol
|
||||
* @return context
|
||||
*/
|
||||
public HttpContext createHttpContext(HttpProtocol httpProtocol) {
|
||||
HttpProtocol.Authorization auth = httpProtocol.getAuthorization();
|
||||
if (auth != null && DispatchConstants.DIGEST_AUTH.equals(auth.getType())) {
|
||||
HttpClientContext clientContext = new HttpClientContext();
|
||||
if (StringUtils.hasText(auth.getDigestAuthUsername())
|
||||
&& StringUtils.hasText(auth.getDigestAuthPassword())) {
|
||||
CredentialsProvider provider = new BasicCredentialsProvider();
|
||||
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(auth.getDigestAuthUsername(),
|
||||
auth.getDigestAuthPassword());
|
||||
provider.setCredentials(AuthScope.ANY, credentials);
|
||||
AuthCache authCache = new BasicAuthCache();
|
||||
authCache.put(new HttpHost(httpProtocol.getHost(), Integer.parseInt(httpProtocol.getPort())), new DigestScheme());
|
||||
clientContext.setCredentialsProvider(provider);
|
||||
clientContext.setAuthCache(authCache);
|
||||
return clientContext;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* create http request
|
||||
*
|
||||
* @param httpProtocol http params
|
||||
* @return http uri request
|
||||
*/
|
||||
public HttpUriRequest createHttpRequest(HttpProtocol httpProtocol) {
|
||||
RequestBuilder requestBuilder;
|
||||
String httpMethod = httpProtocol.getMethod().toUpperCase();
|
||||
if (HttpMethod.GET.matches(httpMethod)) {
|
||||
requestBuilder = RequestBuilder.get();
|
||||
} else if (HttpMethod.POST.matches(httpMethod)) {
|
||||
requestBuilder = RequestBuilder.post();
|
||||
} else if (HttpMethod.PUT.matches(httpMethod)) {
|
||||
requestBuilder = RequestBuilder.put();
|
||||
} else if (HttpMethod.DELETE.matches(httpMethod)) {
|
||||
requestBuilder = RequestBuilder.delete();
|
||||
} else if (HttpMethod.PATCH.matches(httpMethod)) {
|
||||
requestBuilder = RequestBuilder.patch();
|
||||
} else {
|
||||
// not support the method
|
||||
log.error("not support the http method: {}.", httpProtocol.getMethod());
|
||||
return null;
|
||||
}
|
||||
// params
|
||||
Map<String, String> params = httpProtocol.getParams();
|
||||
if (params != null && !params.isEmpty()) {
|
||||
for (Map.Entry<String, String> param : params.entrySet()) {
|
||||
if (StringUtils.hasText(param.getValue())) {
|
||||
requestBuilder.addParameter(param.getKey(), TimeExpressionUtil.calculate(param.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
// The default request header can be overridden if customized
|
||||
// keep-alive
|
||||
requestBuilder.addHeader(HttpHeaders.CONNECTION, "keep-alive");
|
||||
requestBuilder.addHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36");
|
||||
// headers The custom request header is overwritten here
|
||||
Map<String, String> headers = httpProtocol.getHeaders();
|
||||
if (headers != null && !headers.isEmpty()) {
|
||||
for (Map.Entry<String, String> header : headers.entrySet()) {
|
||||
if (StringUtils.hasText(header.getValue())) {
|
||||
requestBuilder.addHeader(CollectUtil.replaceUriSpecialChar(header.getKey()),
|
||||
CollectUtil.replaceUriSpecialChar(header.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
// add accept
|
||||
if (DispatchConstants.PARSE_DEFAULT.equals(httpProtocol.getParseType())
|
||||
|| DispatchConstants.PARSE_JSON_PATH.equals(httpProtocol.getParseType())) {
|
||||
requestBuilder.addHeader(HttpHeaders.ACCEPT, "application/json");
|
||||
} else if (DispatchConstants.PARSE_XML_PATH.equals(httpProtocol.getParseType())) {
|
||||
requestBuilder.addHeader(HttpHeaders.ACCEPT, "text/xml,application/xml");
|
||||
} else {
|
||||
requestBuilder.addHeader(HttpHeaders.ACCEPT, "*/*");
|
||||
}
|
||||
|
||||
if (httpProtocol.getAuthorization() != null) {
|
||||
HttpProtocol.Authorization authorization = httpProtocol.getAuthorization();
|
||||
if (DispatchConstants.BEARER_TOKEN.equalsIgnoreCase(authorization.getType())) {
|
||||
String value = DispatchConstants.BEARER + " " + authorization.getBearerTokenToken();
|
||||
requestBuilder.addHeader(HttpHeaders.AUTHORIZATION, value);
|
||||
} else if (DispatchConstants.BASIC_AUTH.equals(authorization.getType())) {
|
||||
if (StringUtils.hasText(authorization.getBasicAuthUsername())
|
||||
&& StringUtils.hasText(authorization.getBasicAuthPassword())) {
|
||||
String authStr = authorization.getBasicAuthUsername() + ":" + authorization.getBasicAuthPassword();
|
||||
String encodedAuth = new String(Base64.encodeBase64(authStr.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
|
||||
requestBuilder.addHeader(HttpHeaders.AUTHORIZATION, DispatchConstants.BASIC + " " + encodedAuth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if it has payload, would override post params
|
||||
if (StringUtils.hasLength(httpProtocol.getPayload())) {
|
||||
requestBuilder.setEntity(new StringEntity(httpProtocol.getPayload(), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
// uri
|
||||
String uri = CollectUtil.replaceUriSpecialChar(httpProtocol.getUrl());
|
||||
if (IpDomainUtil.isHasSchema(httpProtocol.getHost())) {
|
||||
|
||||
requestBuilder.setUri(httpProtocol.getHost() + ":" + httpProtocol.getPort() + uri);
|
||||
} else {
|
||||
String ipAddressType = IpDomainUtil.checkIpAddressType(httpProtocol.getHost());
|
||||
String baseUri = CollectorConstants.IPV6.equals(ipAddressType)
|
||||
? String.format("[%s]:%s%s", httpProtocol.getHost(), httpProtocol.getPort(), uri)
|
||||
: String.format("%s:%s%s", httpProtocol.getHost(), httpProtocol.getPort(), uri);
|
||||
boolean ssl = Boolean.parseBoolean(httpProtocol.getSsl());
|
||||
if (ssl) {
|
||||
requestBuilder.setUri(CollectorConstants.HTTPS_HEADER + baseUri);
|
||||
} else {
|
||||
requestBuilder.setUri(CollectorConstants.HTTP_HEADER + baseUri);
|
||||
}
|
||||
}
|
||||
|
||||
// custom timeout
|
||||
int timeout = CollectUtil.getTimeout(httpProtocol.getTimeout(), 0);
|
||||
if (timeout > 0) {
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(timeout)
|
||||
.setSocketTimeout(timeout)
|
||||
.setRedirectsEnabled(true)
|
||||
.build();
|
||||
requestBuilder.setConfig(requestConfig);
|
||||
}
|
||||
return requestBuilder.build();
|
||||
}
|
||||
|
||||
private boolean checkSuccessInvoke(Metrics metrics, int statusCode) {
|
||||
List<String> successCodes = metrics.getHttp().getSuccessCodes();
|
||||
Set<Integer> successCodeSet = successCodes != null ? successCodes.stream().map(code -> {
|
||||
try {
|
||||
return Integer.valueOf(code);
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
}).filter(Objects::nonNull).collect(Collectors.toSet()) : defaultSuccessStatusCodes;
|
||||
if (successCodeSet.isEmpty()) {
|
||||
successCodeSet = defaultSuccessStatusCodes;
|
||||
}
|
||||
return successCodeSet.contains(statusCode);
|
||||
}
|
||||
}
|
||||
-163
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.http;
|
||||
|
||||
import static org.apache.hertzbeat.common.constants.SignConstants.RIGHT_DASH;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Date;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollect;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.common.constants.CollectorConstants;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.HttpProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.apache.hertzbeat.common.util.IpDomainUtil;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* ssl Certificate
|
||||
*/
|
||||
@Slf4j
|
||||
public class SslCertificateCollectImpl extends AbstractCollect {
|
||||
|
||||
private static final String NAME_SUBJECT = "subject";
|
||||
private static final String NAME_EXPIRED = "expired";
|
||||
private static final String NAME_START_TIME = "start_time";
|
||||
private static final String NAME_START_TIMESTAMP = "start_timestamp";
|
||||
private static final String NAME_END_TIME = "end_time";
|
||||
private static final String NAME_END_TIMESTAMP = "end_timestamp";
|
||||
|
||||
@Override
|
||||
public void preCheck(Metrics metrics) throws IllegalArgumentException {
|
||||
if (metrics == null || metrics.getHttp() == null) {
|
||||
throw new IllegalArgumentException("Http/Https collect must has http params");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder,
|
||||
long monitorId, String app, Metrics metrics) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
HttpProtocol httpProtocol = metrics.getHttp();
|
||||
String url = httpProtocol.getUrl();
|
||||
if (!StringUtils.hasText(url) || !url.startsWith(RIGHT_DASH)) {
|
||||
httpProtocol.setUrl(StringUtils.hasText(url) ? RIGHT_DASH + url.trim() : RIGHT_DASH);
|
||||
}
|
||||
|
||||
HttpsURLConnection urlConnection = null;
|
||||
try {
|
||||
String uri = "";
|
||||
if (IpDomainUtil.isHasSchema(httpProtocol.getHost())) {
|
||||
uri = httpProtocol.getHost() + ":" + httpProtocol.getPort();
|
||||
} else {
|
||||
uri = "https://" + httpProtocol.getHost() + ":" + httpProtocol.getPort();
|
||||
}
|
||||
urlConnection = (HttpsURLConnection) new URL(uri).openConnection();
|
||||
urlConnection.connect();
|
||||
Certificate[] certificates = urlConnection.getServerCertificates();
|
||||
if (certificates == null || certificates.length == 0) {
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("Ssl certificate does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
long responseTime = System.currentTimeMillis() - startTime;
|
||||
for (Certificate certificate : urlConnection.getServerCertificates()) {
|
||||
X509Certificate x509Certificate = (X509Certificate) certificate;
|
||||
Date now = new Date();
|
||||
Date deadline = x509Certificate.getNotAfter();
|
||||
boolean expired = deadline != null && now.after(deadline);
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String alias : metrics.getAliasFields()) {
|
||||
if (CollectorConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(Long.toString(responseTime));
|
||||
} else if (NAME_SUBJECT.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(x509Certificate.getSubjectX500Principal().getName());
|
||||
} else if (NAME_EXPIRED.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(Boolean.toString(expired));
|
||||
} else if (NAME_START_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(x509Certificate.getNotBefore().toLocaleString());
|
||||
} else if (NAME_START_TIMESTAMP.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(String.valueOf(x509Certificate.getNotBefore().getTime()));
|
||||
} else if (NAME_END_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(x509Certificate.getNotAfter().toLocaleString());
|
||||
} else if (NAME_END_TIMESTAMP.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(String.valueOf(x509Certificate.getNotAfter().getTime()));
|
||||
} else {
|
||||
valueRowBuilder.addColumns(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
} catch (SSLPeerUnverifiedException e1) {
|
||||
String errorMsg = "Ssl certificate does not exist.";
|
||||
if (e1.getMessage() != null) {
|
||||
errorMsg = e1.getMessage();
|
||||
log.error(errorMsg);
|
||||
}
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg(errorMsg);
|
||||
} catch (UnknownHostException e2) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e2);
|
||||
log.info(errorMsg);
|
||||
builder.setCode(CollectRep.Code.UN_REACHABLE);
|
||||
builder.setMsg("unknown host:" + errorMsg);
|
||||
} catch (InterruptedIOException | ConnectException | SSLException e3) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e3);
|
||||
log.info(errorMsg);
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg(errorMsg);
|
||||
} catch (IOException e4) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e4);
|
||||
log.info(errorMsg);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg(errorMsg);
|
||||
} catch (Exception e) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e);
|
||||
log.error(errorMsg, e);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg(errorMsg);
|
||||
} finally {
|
||||
if (urlConnection != null) {
|
||||
urlConnection.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportProtocol() {
|
||||
return DispatchConstants.PROTOCOL_SSL_CERT;
|
||||
}
|
||||
|
||||
private void validateParams(Metrics metrics) {
|
||||
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.http.promethus;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* prometheus parse creater
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PrometheusParseCreater implements InitializingBean {
|
||||
private static AbstractPrometheusParse PROMETHEUSPARSE = new PrometheusVectorParser();
|
||||
|
||||
private static void create() {
|
||||
PROMETHEUSPARSE.setInstance(new PrometheusMatrixParser().setInstance(new PrometheusLastParser()));
|
||||
}
|
||||
|
||||
public static AbstractPrometheusParse getPrometheusParse(){
|
||||
return PROMETHEUSPARSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
create();
|
||||
}
|
||||
}
|
||||
-429
@@ -1,429 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.http.promethus.exporter;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.http.promethus.ParseException;
|
||||
import org.apache.hertzbeat.common.util.StrBuffer;
|
||||
|
||||
/**
|
||||
* Resolves the data passed by prometheus's exporter interface http:xxx/metrics
|
||||
* Reference: prometheus text_parse.go code, entry: TextToMetricFamilies
|
||||
*/
|
||||
@Slf4j
|
||||
public class ExporterParser {
|
||||
private static final String HELP = "HELP";
|
||||
private static final String TYPE = "TYPE";
|
||||
private static final String EOF = "EOF";
|
||||
private static final String METRIC_NAME_LABEL = ".name";
|
||||
private static final String QUANTILE_LABEL = "quantile";
|
||||
private static final String BUCKET_LABEL = "le";
|
||||
private static final String NAME_LABEL = "__name__";
|
||||
private static final String SUM_SUFFIX = "_sum";
|
||||
private static final String COUNT_SUFFIX = "_count";
|
||||
|
||||
private static final char LEFT_CURLY_BRACKET = '{';
|
||||
private static final char RIGHT_CURLY_BRACKET = '}';
|
||||
private static final char EQUALS = '=';
|
||||
private static final char QUOTES = '"';
|
||||
private static final char ENTER = '\n';
|
||||
private static final char SPACE = ' ';
|
||||
private static final char COMMA = ',';
|
||||
private final Lock lock = new ReentrantLock();
|
||||
private MetricFamily currentMetricFamily;
|
||||
private String currentQuantile;
|
||||
private String currentBucket;
|
||||
|
||||
public Map<String, MetricFamily> textToMetric(String resp) {
|
||||
// key: metric name, value: metric family
|
||||
Map<String, MetricFamily> metricMap = new ConcurrentHashMap<>(10);
|
||||
lock.lock();
|
||||
try {
|
||||
String[] lines = resp.split("\n");
|
||||
for (String line : lines) {
|
||||
this.parseLine(metricMap, new StrBuffer(line));
|
||||
}
|
||||
return metricMap;
|
||||
} catch (Exception e) {
|
||||
log.error("parse prometheus exporter data error, msg: {}", e.getMessage());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return metricMap;
|
||||
}
|
||||
|
||||
private void parseLine(Map<String, MetricFamily> metricMap, StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) return;
|
||||
switch (buffer.charAt(0)) {
|
||||
case '#' -> {
|
||||
buffer.read();
|
||||
this.currentMetricFamily = null;
|
||||
this.parseComment(metricMap, buffer);
|
||||
}
|
||||
case ENTER -> {
|
||||
}
|
||||
default -> {
|
||||
this.currentBucket = null;
|
||||
this.currentQuantile = null;
|
||||
this.parseMetric(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseComment(Map<String, MetricFamily> metricMap, StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String token = this.readTokenUnitWhitespace(buffer);
|
||||
if (EOF.equals(token)) {
|
||||
return;
|
||||
}
|
||||
if (!HELP.equals(token) && !TYPE.equals(token)) {
|
||||
log.error("parse comment error {}, start without {} or {}", buffer.toStr(), HELP, TYPE);
|
||||
return;
|
||||
}
|
||||
String metricName = this.readTokenAsMetricName(buffer);
|
||||
this.currentMetricFamily = metricMap.computeIfAbsent(metricName, key -> new MetricFamily());
|
||||
this.currentMetricFamily.setName(metricName);
|
||||
switch (token) {
|
||||
case HELP -> this.parseHelp(buffer);
|
||||
case TYPE -> this.parseType(buffer);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseHelp(StrBuffer line) {
|
||||
line.skipBlankTabs();
|
||||
this.currentMetricFamily.setHelp(line.toStr());
|
||||
}
|
||||
|
||||
private void parseType(StrBuffer line) {
|
||||
line.skipBlankTabs();
|
||||
String type = line.toStr().toLowerCase();
|
||||
MetricType metricType = MetricType.getType(type);
|
||||
if (metricType == null) {
|
||||
throw new ParseException("pare type error");
|
||||
}
|
||||
this.currentMetricFamily.setMetricType(metricType);
|
||||
}
|
||||
|
||||
private void parseMetric(StrBuffer buffer) {
|
||||
String metricName = this.readTokenAsMetricName(buffer);
|
||||
MetricFamily.Label label = new MetricFamily.Label();
|
||||
label.setName(METRIC_NAME_LABEL);
|
||||
label.setValue(metricName);
|
||||
|
||||
if (metricName.isEmpty()) {
|
||||
log.error("error parse metric, metric name is null, line: {}", buffer.toStr());
|
||||
return;
|
||||
}
|
||||
|
||||
List<MetricFamily.Metric> metricList = this.currentMetricFamily.getMetricList();
|
||||
if (metricList == null) {
|
||||
metricList = new ArrayList<>();
|
||||
this.currentMetricFamily.setMetricList(metricList);
|
||||
}
|
||||
|
||||
// TODO For the time being, the data is displayed in the form of labels. If there is a better chart display method in the future, we will optimize it.
|
||||
MetricFamily.Metric metric = new MetricFamily.Metric();
|
||||
metricList.add(metric);
|
||||
|
||||
metric.setLabelPair(new ArrayList<>());
|
||||
metric.getLabelPair().add(label);
|
||||
this.readLabels(metric, buffer);
|
||||
}
|
||||
|
||||
private void readLabels(MetricFamily.Metric metric, StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (buffer.charAt(0) == LEFT_CURLY_BRACKET) {
|
||||
buffer.read();
|
||||
this.startReadLabelName(metric, buffer);
|
||||
} else {
|
||||
this.readLabelValue(metric, null, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private void startReadLabelName(MetricFamily.Metric metric, StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (buffer.charAt(0) == RIGHT_CURLY_BRACKET) {
|
||||
buffer.read();
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
this.readLabelValue(metric, new MetricFamily.Label(), buffer);
|
||||
return;
|
||||
}
|
||||
String labelName = this.readTokenAsLabelName(buffer);
|
||||
if (labelName.isEmpty() || labelName.equals(NAME_LABEL)) {
|
||||
throw new ParseException("invalid label name" + labelName + ", label name size = 0 or label name equals " + NAME_LABEL);
|
||||
}
|
||||
MetricFamily.Label label = new MetricFamily.Label();
|
||||
label.setName(labelName);
|
||||
if (buffer.read() != EQUALS) {
|
||||
throw new ParseException("parse error, not match the format of labelName=labelValue");
|
||||
}
|
||||
this.startReadLabelValue(metric, label, buffer);
|
||||
}
|
||||
|
||||
private void startReadLabelValue(MetricFamily.Metric metric, MetricFamily.Label label, StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
char c = buffer.read();
|
||||
if (c != QUOTES) {
|
||||
throw new ParseException("expected '\"' at start of label value, line: " + buffer.toStr());
|
||||
}
|
||||
String labelValue = this.readTokenAsLabelValue(buffer);
|
||||
label.setValue(labelValue);
|
||||
if (!this.isValidLabelValue(labelValue)) {
|
||||
throw new ParseException("no valid label value: " + labelValue);
|
||||
}
|
||||
if (this.currentMetricFamily.getMetricType().equals(MetricType.SUMMARY) && label.getName().equals(QUANTILE_LABEL)) {
|
||||
this.currentQuantile = labelValue;
|
||||
} else if (this.currentMetricFamily.getMetricType().equals(MetricType.HISTOGRAM) && label.getName().equals(BUCKET_LABEL)) {
|
||||
this.currentBucket = labelValue;
|
||||
}
|
||||
metric.getLabelPair().add(label);
|
||||
|
||||
if (buffer.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
c = buffer.read();
|
||||
switch (c) {
|
||||
case COMMA -> this.startReadLabelName(metric, buffer);
|
||||
case RIGHT_CURLY_BRACKET -> this.readLabelValue(metric, label, buffer);
|
||||
default -> throw new ParseException("expected '}' or ',' at end of label value, line: " + buffer.toStr());
|
||||
}
|
||||
}
|
||||
|
||||
private void readLabelValue(MetricFamily.Metric metric, MetricFamily.Label label, StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
if (buffer.isEmpty()) return;
|
||||
switch (this.currentMetricFamily.getMetricType()) {
|
||||
case INFO -> {
|
||||
MetricFamily.Info info = new MetricFamily.Info();
|
||||
info.setValue(buffer.toDouble());
|
||||
metric.setInfo(info);
|
||||
}
|
||||
case COUNTER -> {
|
||||
MetricFamily.Counter counter = new MetricFamily.Counter();
|
||||
counter.setValue(buffer.toDouble());
|
||||
metric.setCounter(counter);
|
||||
}
|
||||
case GAUGE -> {
|
||||
MetricFamily.Gauge gauge = new MetricFamily.Gauge();
|
||||
gauge.setValue(buffer.toDouble());
|
||||
metric.setGauge(gauge);
|
||||
}
|
||||
case UNTYPED -> {
|
||||
MetricFamily.Untyped untyped = new MetricFamily.Untyped();
|
||||
untyped.setValue(buffer.toDouble());
|
||||
metric.setUntyped(untyped);
|
||||
}
|
||||
case SUMMARY -> {
|
||||
// For the time being, the data is displayed in the form of labels. If there is a better chart display method in the future, we will optimize it.
|
||||
MetricFamily.Summary summary = new MetricFamily.Summary();
|
||||
summary.setValue(buffer.toDouble());
|
||||
metric.setSummary(summary);
|
||||
}
|
||||
case HISTOGRAM -> {
|
||||
// For the time being, the data is displayed in the form of labels. If there is a better chart display method in the future, we will optimize it.
|
||||
MetricFamily.Histogram histogram = new MetricFamily.Histogram();
|
||||
histogram.setValue(buffer.toDouble());
|
||||
metric.setHistogram(histogram);
|
||||
}
|
||||
default -> throw new ParseException("no such type in metricFamily");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the token before the first whitespace
|
||||
*
|
||||
* @param buffer A line data object
|
||||
* @return token unit
|
||||
*/
|
||||
private String readTokenUnitWhitespace(StrBuffer buffer) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while (!buffer.isEmpty()) {
|
||||
char c = buffer.read();
|
||||
if (c == SPACE) {
|
||||
break;
|
||||
}
|
||||
builder.append(c);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the metric
|
||||
*
|
||||
* @param buffer A line data object
|
||||
* @return token name
|
||||
*/
|
||||
private String readTokenAsMetricName(StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (this.isValidMetricNameStart(buffer.charAt(0))) {
|
||||
while (!buffer.isEmpty()) {
|
||||
char c = buffer.read();
|
||||
if (!this.isValidMetricNameContinuation(c)) {
|
||||
buffer.rollback();
|
||||
break;
|
||||
}
|
||||
builder.append(c);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
throw new ParseException("parse metric name error");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the label
|
||||
*
|
||||
* @param buffer A line data object
|
||||
* @return label name
|
||||
*/
|
||||
private String readTokenAsLabelName(StrBuffer buffer) {
|
||||
buffer.skipBlankTabs();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
char c = buffer.read();
|
||||
if (this.isValidLabelNameStart(c)) {
|
||||
builder.append(c);
|
||||
while (!buffer.isEmpty()) {
|
||||
c = buffer.read();
|
||||
if (!this.isValidLabelNameContinuation(c)) {
|
||||
buffer.rollback();
|
||||
break;
|
||||
}
|
||||
builder.append(c);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
throw new ParseException("parse label name error");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the label
|
||||
*
|
||||
* @param buffer A line data object
|
||||
* @return label value
|
||||
*/
|
||||
private String readTokenAsLabelValue(StrBuffer buffer) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
boolean escaped = false;
|
||||
while (!buffer.isEmpty()) {
|
||||
char c = buffer.read();
|
||||
// Handle '\\' escape sequences
|
||||
if (escaped) {
|
||||
switch (c) {
|
||||
case QUOTES, '\\' -> builder.append(c);
|
||||
case 'n' -> builder.append('\n');
|
||||
default -> throw new ParseException("parse label value error");
|
||||
}
|
||||
escaped = false;
|
||||
} else {
|
||||
switch (c) {
|
||||
case QUOTES -> {
|
||||
return builder.toString();
|
||||
}
|
||||
case ENTER -> throw new ParseException("parse label value error, next line");
|
||||
case '\\' -> escaped = true;
|
||||
default -> builder.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a character conforms to the first character rule for metric names
|
||||
*
|
||||
* @param c metric character
|
||||
* @return true/false
|
||||
*/
|
||||
private boolean isValidMetricNameStart(char c) {
|
||||
return isValidLabelNameStart(c) || c == ':';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a character conforms to rules for metric name characters other than the first
|
||||
*
|
||||
* @param c metric character
|
||||
* @return true/false
|
||||
*/
|
||||
private boolean isValidMetricNameContinuation(char c) {
|
||||
return isValidLabelNameContinuation(c) || c == ':';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a character conforms to the first character rule for label names
|
||||
*
|
||||
* @param c metric character
|
||||
* @return true/false
|
||||
*/
|
||||
private boolean isValidLabelNameStart(char c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a character conforms to rules for label name characters other than the first
|
||||
*
|
||||
* @param c metric character
|
||||
* @return true/false
|
||||
*/
|
||||
private boolean isValidLabelNameContinuation(char c) {
|
||||
return isValidLabelNameStart(c) || (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is a valid UTF-8 encoded string
|
||||
*
|
||||
* @param s label value
|
||||
* @return true/false
|
||||
*/
|
||||
private boolean isValidLabelValue(String s) {
|
||||
return s != null && s.equals(new String(s.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
private boolean isSum(String s) {
|
||||
return s != null && s.endsWith(SUM_SUFFIX);
|
||||
}
|
||||
|
||||
private boolean isCount(String s) {
|
||||
return s != null && s.endsWith(COUNT_SUFFIX);
|
||||
}
|
||||
|
||||
}
|
||||
-252
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.http.promethus.exporter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* MetricFamily.
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
public class MetricFamily {
|
||||
/**
|
||||
* metric name
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* metric help
|
||||
*/
|
||||
private String help;
|
||||
|
||||
/**
|
||||
* metric type
|
||||
*/
|
||||
private MetricType metricType;
|
||||
|
||||
/**
|
||||
* Specific metric
|
||||
*/
|
||||
private List<Metric> metricList;
|
||||
|
||||
/**
|
||||
* Metric
|
||||
*/
|
||||
@Data
|
||||
public static class Metric {
|
||||
|
||||
/**
|
||||
* Label data, mainly corresponding to the content within {}
|
||||
*/
|
||||
private List<Label> labelPair;
|
||||
|
||||
/**
|
||||
* info
|
||||
*/
|
||||
private Info info;
|
||||
|
||||
/**
|
||||
* gauge
|
||||
*/
|
||||
private Gauge gauge;
|
||||
|
||||
/**
|
||||
* counter
|
||||
*/
|
||||
private Counter counter;
|
||||
|
||||
/**
|
||||
* summary
|
||||
*/
|
||||
private Summary summary;
|
||||
|
||||
/**
|
||||
* untyped
|
||||
*/
|
||||
private Untyped untyped;
|
||||
|
||||
/**
|
||||
* histogram
|
||||
*/
|
||||
private Histogram histogram;
|
||||
|
||||
/**
|
||||
* timestampMs
|
||||
*/
|
||||
private Long timestampMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Label
|
||||
*/
|
||||
@Data
|
||||
public static class Label {
|
||||
|
||||
/**
|
||||
* name
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private String value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Info
|
||||
*/
|
||||
@Data
|
||||
public static class Info {
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Counter
|
||||
*/
|
||||
@Data
|
||||
public static class Counter {
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
|
||||
// Exemplar
|
||||
}
|
||||
|
||||
/**
|
||||
* Gauge
|
||||
*/
|
||||
@Data
|
||||
public static class Gauge {
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
}
|
||||
|
||||
/**
|
||||
* untyped
|
||||
*/
|
||||
@Data
|
||||
public static class Untyped {
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary
|
||||
*/
|
||||
@Data
|
||||
public static class Summary {
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
|
||||
/**
|
||||
* count
|
||||
*/
|
||||
private long count;
|
||||
|
||||
/**
|
||||
* sum
|
||||
*/
|
||||
private double sum;
|
||||
|
||||
/**
|
||||
* quantileList
|
||||
*/
|
||||
private List<Quantile> quantileList = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Quantile
|
||||
*/
|
||||
@Data
|
||||
public static class Quantile {
|
||||
/**
|
||||
* Corresponding to the quantile field in Prometheus
|
||||
*/
|
||||
private double xLabel;
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Histogram
|
||||
*/
|
||||
@Data
|
||||
public static class Histogram {
|
||||
|
||||
/**
|
||||
* value
|
||||
*/
|
||||
private double value;
|
||||
|
||||
/**
|
||||
* count
|
||||
*/
|
||||
private long count;
|
||||
|
||||
/**
|
||||
* sum
|
||||
*/
|
||||
private double sum;
|
||||
|
||||
/**
|
||||
* bucketList
|
||||
*/
|
||||
private List<Bucket> bucketList = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bucket
|
||||
*/
|
||||
@Data
|
||||
public static class Bucket {
|
||||
|
||||
/**
|
||||
* cumulativeCount
|
||||
*/
|
||||
private long cumulativeCount;
|
||||
|
||||
/**
|
||||
* upperBound
|
||||
*/
|
||||
private double upperBound;
|
||||
}
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.http.promethus.exporter;
|
||||
|
||||
/**
|
||||
* prometheus metrics type
|
||||
*/
|
||||
public enum MetricType {
|
||||
// for string metric info
|
||||
INFO("info"),
|
||||
// Represents a monotonically increasing counter, e.g., counting occurrences
|
||||
COUNTER("counter"),
|
||||
// A metric type that can fluctuate up and down, e.g., CPU usage rate
|
||||
GAUGE("gauge"),
|
||||
SUMMARY("summary"),
|
||||
UNTYPED("untyped"),
|
||||
HISTOGRAM("histogram");
|
||||
|
||||
private final String value;
|
||||
|
||||
MetricType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static MetricType getType(String value) {
|
||||
for (MetricType metricType : values()) {
|
||||
if (metricType.getValue().equals(value)) {
|
||||
return metricType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
-127
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.httpsd;
|
||||
|
||||
import com.ecwid.consul.transport.TransportException;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Objects;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollect;
|
||||
import org.apache.hertzbeat.collector.collect.httpsd.discovery.DiscoveryClient;
|
||||
import org.apache.hertzbeat.collector.collect.httpsd.discovery.DiscoveryClientManagement;
|
||||
import org.apache.hertzbeat.collector.collect.httpsd.discovery.entity.ServerInfo;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.common.constants.CollectorConstants;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.HttpsdProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
|
||||
/**
|
||||
* http_sd protocol collection implementation
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpsdImpl extends AbstractCollect {
|
||||
private static final String SERVER = "server";
|
||||
|
||||
@Setter
|
||||
@VisibleForTesting
|
||||
private DiscoveryClientManagement discoveryClientManagement = new DiscoveryClientManagement();
|
||||
|
||||
@Override
|
||||
public void preCheck(Metrics metrics) throws IllegalArgumentException {
|
||||
HttpsdProtocol httpsdProtocol = metrics.getHttpsd();
|
||||
if (Objects.isNull(httpsdProtocol) || httpsdProtocol.isInvalid()){
|
||||
throw new IllegalArgumentException("http_sd collect must have a valid http_sd protocol param! ");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder, long monitorId, String app, Metrics metrics) {
|
||||
HttpsdProtocol httpsdProtocol = metrics.getHttpsd();
|
||||
|
||||
try (DiscoveryClient discoveryClient = discoveryClientManagement.getClient(httpsdProtocol)) {
|
||||
collectMetrics(builder, metrics, discoveryClient);
|
||||
} catch (TransportException e1) {
|
||||
String errorMsg = "Consul " + CommonUtil.getMessageFromThrowable(e1);
|
||||
log.error(errorMsg);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg(errorMsg);
|
||||
} catch (Exception e) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e);
|
||||
log.error(errorMsg, e);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private void collectMetrics(CollectRep.MetricsData.Builder builder, Metrics metrics, DiscoveryClient discoveryClient) {
|
||||
long beginTime = System.currentTimeMillis();
|
||||
// Available and Server monitor
|
||||
if (StringUtils.equals(metrics.getName(), SERVER)) {
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
ServerInfo serverInfo = discoveryClient.getServerInfo();
|
||||
metrics.getAliasFields().forEach(fieldName -> {
|
||||
if (StringUtils.equalsAnyIgnoreCase(CollectorConstants.RESPONSE_TIME, fieldName)) {
|
||||
valueRowBuilder.addColumns(String.valueOf(System.currentTimeMillis() - beginTime));
|
||||
} else {
|
||||
addColumnIfMatched(fieldName, serverInfo, valueRowBuilder);
|
||||
}
|
||||
});
|
||||
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
} else {
|
||||
// Service instances monitor
|
||||
discoveryClient.getServices().forEach(serviceInstance -> {
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
metrics.getAliasFields().forEach(fieldName -> addColumnIfMatched(fieldName, serviceInstance, valueRowBuilder));
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportProtocol() {
|
||||
return DispatchConstants.PROTOCOL_HTTP_SD;
|
||||
}
|
||||
|
||||
private boolean checkParamsFailed(HttpsdProtocol httpsd) {
|
||||
return Objects.isNull(httpsd) || httpsd.isInvalid();
|
||||
}
|
||||
|
||||
private void addColumnIfMatched(String fieldName, Object sourceObj, CollectRep.ValueRow.Builder valueRowBuilder) {
|
||||
String columnValue = null;
|
||||
try {
|
||||
Field declaredField = sourceObj.getClass().getDeclaredField(fieldName);
|
||||
declaredField.setAccessible(Boolean.TRUE);
|
||||
columnValue = String.valueOf(declaredField.get(sourceObj));
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
log.warn("No such field for {}", fieldName);
|
||||
}
|
||||
|
||||
valueRowBuilder.addColumns(StringUtils.isBlank(columnValue)
|
||||
? CommonConstants.NULL_VALUE
|
||||
: columnValue);
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.httpsd.discovery;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.collector.collect.httpsd.discovery.entity.ConnectConfig;
|
||||
import org.apache.hertzbeat.collector.collect.httpsd.discovery.entity.ServerInfo;
|
||||
import org.apache.hertzbeat.collector.collect.httpsd.discovery.entity.ServiceInstance;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.HttpsdProtocol;
|
||||
|
||||
/**
|
||||
* DiscoveryClient interface.
|
||||
*/
|
||||
public interface DiscoveryClient extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* Build connect config.
|
||||
* @param httpsdProtocol httpsd protocol.
|
||||
* @return connect config object.
|
||||
*/
|
||||
ConnectConfig buildConnectConfig(HttpsdProtocol httpsdProtocol);
|
||||
|
||||
/**
|
||||
* Initialize client.
|
||||
* @param connectConfig connect config.
|
||||
*/
|
||||
void initClient(ConnectConfig connectConfig);
|
||||
|
||||
/**
|
||||
* Get server info.
|
||||
* @return server info object.
|
||||
*/
|
||||
ServerInfo getServerInfo();
|
||||
|
||||
/**
|
||||
* Get services.
|
||||
* @return service instance list.
|
||||
*/
|
||||
List<ServiceInstance> getServices();
|
||||
|
||||
/**
|
||||
* Discovery client Health check.
|
||||
* @return true if health check pass, otherwise false
|
||||
*/
|
||||
boolean healthCheck();
|
||||
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.httpsd.discovery;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.apache.hertzbeat.collector.collect.httpsd.constant.DiscoveryClientInstance;
|
||||
import org.apache.hertzbeat.collector.collect.httpsd.discovery.impl.ConsulDiscoveryClient;
|
||||
import org.apache.hertzbeat.collector.collect.httpsd.discovery.impl.NacosDiscoveryClient;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.HttpsdProtocol;
|
||||
|
||||
/**
|
||||
* Discovery Client Management
|
||||
*/
|
||||
public class DiscoveryClientManagement {
|
||||
|
||||
public DiscoveryClient getClient(HttpsdProtocol httpsdProtocol) {
|
||||
return createClient(httpsdProtocol, DiscoveryClientInstance.getByName(httpsdProtocol.getDiscoveryClientTypeName()));
|
||||
}
|
||||
|
||||
private DiscoveryClient createClient(HttpsdProtocol httpsdProtocol, DiscoveryClientInstance discoveryClientInstance) {
|
||||
if (Objects.equals(discoveryClientInstance, DiscoveryClientInstance.NOT_SUPPORT)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return doCreateClient(httpsdProtocol, discoveryClientInstance);
|
||||
}
|
||||
|
||||
private DiscoveryClient doCreateClient(HttpsdProtocol httpsdProtocol, DiscoveryClientInstance discoveryClientInstance) {
|
||||
DiscoveryClient discoveryClient;
|
||||
switch (discoveryClientInstance) {
|
||||
case CONSUL -> discoveryClient = new ConsulDiscoveryClient();
|
||||
case NACOS -> discoveryClient = new NacosDiscoveryClient();
|
||||
default -> { return null; }
|
||||
}
|
||||
discoveryClient.initClient(discoveryClient.buildConnectConfig(httpsdProtocol));
|
||||
return discoveryClient;
|
||||
}
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.httpsd.discovery.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Server Info
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ServerInfo {
|
||||
private String address;
|
||||
private String port;
|
||||
}
|
||||
-218
@@ -1,218 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.jmx;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.management.Attribute;
|
||||
import javax.management.AttributeList;
|
||||
import javax.management.MBeanAttributeInfo;
|
||||
import javax.management.MBeanFeatureInfo;
|
||||
import javax.management.MBeanInfo;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.ObjectInstance;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.openmbean.CompositeData;
|
||||
import javax.management.openmbean.CompositeType;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
import javax.naming.Context;
|
||||
import javax.rmi.ssl.SslRMIClientSocketFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollect;
|
||||
import org.apache.hertzbeat.collector.collect.common.cache.CacheIdentifier;
|
||||
import org.apache.hertzbeat.collector.collect.common.cache.ConnectionCommonCache;
|
||||
import org.apache.hertzbeat.collector.collect.common.cache.JmxConnect;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.JmxProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* jmx protocol acquisition implementation
|
||||
*/
|
||||
@Slf4j
|
||||
public class JmxCollectImpl extends AbstractCollect {
|
||||
|
||||
private static final String JMX_URL_PREFIX = "service:jmx:rmi:///jndi/rmi://";
|
||||
|
||||
private static final String JMX_URL_SUFFIX = "/jmxrmi";
|
||||
|
||||
private static final String IGNORED_STUB = "/stub/";
|
||||
|
||||
private static final String SUB_ATTRIBUTE = "->";
|
||||
private final ConnectionCommonCache<CacheIdentifier, JmxConnect> connectionCommonCache;
|
||||
|
||||
public JmxCollectImpl() {
|
||||
connectionCommonCache = new ConnectionCommonCache<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preCheck(Metrics metrics) throws IllegalArgumentException {
|
||||
Assert.isTrue(metrics != null && metrics.getJmx() != null, "JMX collect must have JMX params");
|
||||
|
||||
String url = metrics.getJmx().getUrl();
|
||||
if (StringUtils.hasText(url)) {
|
||||
Assert.doesNotContain(url, IGNORED_STUB, "JMX url prohibit contains stub, please check");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder, long monitorId, String app, Metrics metrics) {
|
||||
|
||||
try {
|
||||
JmxProtocol jmxProtocol = metrics.getJmx();
|
||||
|
||||
// Create a jndi remote connection
|
||||
JMXConnector jmxConnector = getConnectSession(jmxProtocol);
|
||||
|
||||
MBeanServerConnection serverConnection = jmxConnector.getMBeanServerConnection();
|
||||
ObjectName objectName = new ObjectName(jmxProtocol.getObjectName());
|
||||
|
||||
Set<ObjectInstance> objectInstanceSet = serverConnection.queryMBeans(objectName, null);
|
||||
Set<String> attributeNameSet = metrics.getAliasFields().stream()
|
||||
.map(field -> field.split(SUB_ATTRIBUTE)[0]).collect(Collectors.toSet());
|
||||
for (ObjectInstance objectInstance : objectInstanceSet) {
|
||||
ObjectName currentObjectName = objectInstance.getObjectName();
|
||||
MBeanInfo beanInfo = serverConnection.getMBeanInfo(currentObjectName);
|
||||
MBeanAttributeInfo[] attrInfos = beanInfo.getAttributes();
|
||||
String[] attributes = new String[attributeNameSet.size()];
|
||||
attributes = Arrays.stream(attrInfos)
|
||||
.filter(item -> item.isReadable() && attributeNameSet.contains(item.getName()))
|
||||
.map(MBeanFeatureInfo::getName)
|
||||
.collect(Collectors.toList()).toArray(attributes);
|
||||
AttributeList attributeList = serverConnection.getAttributes(currentObjectName, attributes);
|
||||
|
||||
Map<String, String> attributeValueMap = extractAttributeValue(attributeList);
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String aliasField : metrics.getAliasFields()) {
|
||||
String fieldValue = attributeValueMap.get(aliasField);
|
||||
valueRowBuilder.addColumns(fieldValue != null ? fieldValue : CommonConstants.NULL_VALUE);
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
} catch (IOException exception) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(exception);
|
||||
log.error("JMX IOException :{}", errorMsg);
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg(errorMsg);
|
||||
} catch (Exception e) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(e);
|
||||
log.error("JMX Error :{}", errorMsg);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportProtocol() {
|
||||
return DispatchConstants.PROTOCOL_JMX;
|
||||
}
|
||||
|
||||
private Map<String, String> extractAttributeValue(AttributeList attributeList) {
|
||||
if (attributeList == null || attributeList.isEmpty()) {
|
||||
throw new RuntimeException("attributeList is empty");
|
||||
}
|
||||
Map<String, String> attributeValueMap = new HashMap<>(attributeList.size());
|
||||
for (Attribute attribute : attributeList.asList()) {
|
||||
Object value = attribute.getValue();
|
||||
if (value == null) {
|
||||
log.info("attribute {} value is null.", attribute.getName());
|
||||
continue;
|
||||
}
|
||||
if (value instanceof Number || value instanceof String || value instanceof ObjectName
|
||||
|| value instanceof Boolean || value instanceof Date || value instanceof TimeUnit) {
|
||||
attributeValueMap.put(attribute.getName(), value.toString());
|
||||
} else if (value instanceof CompositeData compositeData) {
|
||||
CompositeType compositeType = compositeData.getCompositeType();
|
||||
for (String typeKey : compositeType.keySet()) {
|
||||
Object fieldValue = compositeData.get(typeKey);
|
||||
attributeValueMap.put(attribute.getName() + SUB_ATTRIBUTE + typeKey, fieldValue.toString());
|
||||
}
|
||||
} else if (value instanceof String[] values) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int index = 0; index < values.length; index++) {
|
||||
builder.append(values[index]);
|
||||
if (index < values.length - 1) {
|
||||
builder.append(",");
|
||||
}
|
||||
}
|
||||
attributeValueMap.put(attribute.getName(), builder.toString());
|
||||
} else {
|
||||
log.warn("attribute value type {} not support.", value.getClass().getName());
|
||||
}
|
||||
}
|
||||
return attributeValueMap;
|
||||
}
|
||||
|
||||
private JMXConnector getConnectSession(JmxProtocol jmxProtocol) throws IOException {
|
||||
CacheIdentifier identifier = CacheIdentifier.builder().ip(jmxProtocol.getHost())
|
||||
.port(jmxProtocol.getPort()).username(jmxProtocol.getUsername())
|
||||
.password(jmxProtocol.getPassword()).build();
|
||||
Optional<JmxConnect> cacheOption = connectionCommonCache.getCache(identifier, true);
|
||||
JMXConnector conn = null;
|
||||
if (cacheOption.isPresent()) {
|
||||
JmxConnect jmxConnect = cacheOption.get();
|
||||
conn = jmxConnect.getConnection();
|
||||
try {
|
||||
conn.getMBeanServerConnection();
|
||||
} catch (Exception e) {
|
||||
conn = null;
|
||||
connectionCommonCache.removeCache(identifier);
|
||||
}
|
||||
}
|
||||
if (conn != null) {
|
||||
return conn;
|
||||
}
|
||||
String url;
|
||||
if (jmxProtocol.getUrl() != null) {
|
||||
url = jmxProtocol.getUrl();
|
||||
} else {
|
||||
url = JMX_URL_PREFIX + jmxProtocol.getHost() + ":" + jmxProtocol.getPort() + JMX_URL_SUFFIX;
|
||||
}
|
||||
Map<String, Object> environment = new HashMap<>(4);
|
||||
if (StringUtils.hasText(jmxProtocol.getUsername()) && StringUtils.hasText(jmxProtocol.getPassword())) {
|
||||
String[] credential = new String[] {jmxProtocol.getUsername(), jmxProtocol.getPassword()};
|
||||
environment.put(javax.management.remote.JMXConnector.CREDENTIALS, credential);
|
||||
}
|
||||
if (Boolean.TRUE.toString().equals(jmxProtocol.getSsl())) {
|
||||
environment.put(Context.SECURITY_PROTOCOL, "ssl");
|
||||
SslRMIClientSocketFactory clientSocketFactory = new SslRMIClientSocketFactory();
|
||||
environment.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, clientSocketFactory);
|
||||
environment.put("com.sun.jndi.rmi.factory.socket", clientSocketFactory);
|
||||
}
|
||||
JMXServiceURL jmxServiceUrl = new JMXServiceURL(url);
|
||||
conn = JMXConnectorFactory.connect(jmxServiceUrl, environment);
|
||||
connectionCommonCache.addCache(identifier, new JmxConnect(conn));
|
||||
return conn;
|
||||
}
|
||||
|
||||
}
|
||||
-330
@@ -1,330 +0,0 @@
|
||||
/*
|
||||
* 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.collector.collect.ssh;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollect;
|
||||
import org.apache.hertzbeat.collector.collect.common.cache.CacheIdentifier;
|
||||
import org.apache.hertzbeat.collector.collect.common.cache.ConnectionCommonCache;
|
||||
import org.apache.hertzbeat.collector.collect.common.cache.SshConnect;
|
||||
import org.apache.hertzbeat.collector.collect.common.ssh.CommonSshClient;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
|
||||
import org.apache.hertzbeat.collector.util.CollectUtil;
|
||||
import org.apache.hertzbeat.collector.util.PrivateKeyUtils;
|
||||
import org.apache.hertzbeat.common.constants.CollectorConstants;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.job.protocol.SshProtocol;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.apache.sshd.client.SshClient;
|
||||
import org.apache.sshd.client.channel.ClientChannel;
|
||||
import org.apache.sshd.client.channel.ClientChannelEvent;
|
||||
import org.apache.sshd.client.session.ClientSession;
|
||||
import org.apache.sshd.common.SshException;
|
||||
import org.apache.sshd.common.channel.exception.SshChannelOpenException;
|
||||
import org.apache.sshd.common.util.io.output.NoCloseOutputStream;
|
||||
import org.apache.sshd.common.util.security.SecurityUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Ssh protocol collection implementation
|
||||
*/
|
||||
@Slf4j
|
||||
public class SshCollectImpl extends AbstractCollect {
|
||||
|
||||
private static final String PARSE_TYPE_ONE_ROW = "oneRow";
|
||||
private static final String PARSE_TYPE_MULTI_ROW = "multiRow";
|
||||
private static final String PARSE_TYPE_NETCAT = "netcat";
|
||||
private static final String PARSE_TYPE_LOG = "log";
|
||||
|
||||
private static final int DEFAULT_TIMEOUT = 10_000;
|
||||
private final ConnectionCommonCache<CacheIdentifier, SshConnect> connectionCommonCache;
|
||||
|
||||
public SshCollectImpl() {
|
||||
connectionCommonCache = new ConnectionCommonCache<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preCheck(Metrics metrics) throws IllegalArgumentException {
|
||||
if (metrics == null || metrics.getSsh() == null) {
|
||||
throw new IllegalArgumentException("ssh collect must has ssh params");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(CollectRep.MetricsData.Builder builder, long monitorId, String app, Metrics metrics) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
SshProtocol sshProtocol = metrics.getSsh();
|
||||
boolean reuseConnection = Boolean.parseBoolean(sshProtocol.getReuseConnection());
|
||||
int timeout = CollectUtil.getTimeout(sshProtocol.getTimeout(), DEFAULT_TIMEOUT);
|
||||
ClientChannel channel = null;
|
||||
ClientSession clientSession = null;
|
||||
try {
|
||||
clientSession = getConnectSession(sshProtocol, timeout, reuseConnection);
|
||||
channel = clientSession.createExecChannel(sshProtocol.getScript());
|
||||
ByteArrayOutputStream response = new ByteArrayOutputStream();
|
||||
channel.setOut(response);
|
||||
channel.setErr(new NoCloseOutputStream(System.err));
|
||||
channel.open().verify(timeout);
|
||||
List<ClientChannelEvent> list = new ArrayList<>();
|
||||
list.add(ClientChannelEvent.CLOSED);
|
||||
Collection<ClientChannelEvent> waitEvents = channel.waitFor(list, timeout);
|
||||
if (waitEvents.contains(ClientChannelEvent.TIMEOUT)) {
|
||||
throw new SocketTimeoutException("Failed to retrieve command result in time: " + sshProtocol.getScript());
|
||||
}
|
||||
Long responseTime = System.currentTimeMillis() - startTime;
|
||||
String result = response.toString();
|
||||
if (!StringUtils.hasText(result)) {
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("ssh shell response data is null");
|
||||
return;
|
||||
}
|
||||
switch (sshProtocol.getParseType()) {
|
||||
case PARSE_TYPE_LOG -> parseResponseDataByLog(result, metrics.getAliasFields(), builder, responseTime);
|
||||
case PARSE_TYPE_NETCAT -> parseResponseDataByNetcat(result, metrics.getAliasFields(), builder, responseTime);
|
||||
case PARSE_TYPE_ONE_ROW -> parseResponseDataByOne(result, metrics.getAliasFields(), builder, responseTime);
|
||||
case PARSE_TYPE_MULTI_ROW -> parseResponseDataByMulti(result, metrics.getAliasFields(), builder, responseTime);
|
||||
default -> {
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg("Ssh collect not support this parse type: " + sshProtocol.getParseType());
|
||||
}
|
||||
}
|
||||
} catch (ConnectException connectException) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(connectException);
|
||||
log.info(errorMsg);
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg("The peer refused to connect: service port does not listening or firewall: " + errorMsg);
|
||||
} catch (SshException sshException) {
|
||||
Throwable throwable = sshException.getCause();
|
||||
if (throwable instanceof SshChannelOpenException) {
|
||||
log.warn("Remote ssh server no more session channel, please increase sshd_config MaxSessions.");
|
||||
}
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(sshException);
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg("Peer ssh connection failed: " + errorMsg);
|
||||
} catch (IOException ioException) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(ioException);
|
||||
log.info(errorMsg);
|
||||
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
|
||||
builder.setMsg("Peer io connection failed: " + errorMsg);
|
||||
} catch (Exception exception) {
|
||||
String errorMsg = CommonUtil.getMessageFromThrowable(exception);
|
||||
log.warn(errorMsg, exception);
|
||||
builder.setCode(CollectRep.Code.FAIL);
|
||||
builder.setMsg(errorMsg);
|
||||
} finally {
|
||||
if (channel != null && channel.isOpen()) {
|
||||
try {
|
||||
channel.close();
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
if (clientSession != null && !reuseConnection) {
|
||||
try {
|
||||
clientSession.close();
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportProtocol() {
|
||||
return DispatchConstants.PROTOCOL_SSH;
|
||||
}
|
||||
|
||||
private void parseResponseDataByLog(String result, List<String> aliasFields, CollectRep.MetricsData.Builder builder, Long responseTime) {
|
||||
String[] lines = result.split("\n");
|
||||
if (lines.length + 1 < aliasFields.size()) {
|
||||
log.error("ssh response data not enough: {}", result);
|
||||
return;
|
||||
}
|
||||
for (String line : lines) {
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String alias : aliasFields) {
|
||||
if (CollectorConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(responseTime.toString());
|
||||
} else {
|
||||
valueRowBuilder.addColumns(line);
|
||||
}
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private void parseResponseDataByNetcat(String result, List<String> aliasFields, CollectRep.MetricsData.Builder builder, Long responseTime) {
|
||||
String[] lines = result.split("\n");
|
||||
if (lines.length + 1 < aliasFields.size()) {
|
||||
log.error("ssh response data not enough: {}", result);
|
||||
return;
|
||||
}
|
||||
boolean contains = lines[0].contains("=");
|
||||
Map<String, String> mapValue = Arrays.stream(lines)
|
||||
.map(item -> {
|
||||
if (contains) {
|
||||
return item.split("=");
|
||||
} else {
|
||||
return item.split("\t");
|
||||
}
|
||||
})
|
||||
.filter(item -> item.length == 2)
|
||||
.collect(Collectors.toMap(x -> x[0], x -> x[1]));
|
||||
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String field : aliasFields) {
|
||||
String fieldValue = mapValue.get(field);
|
||||
if (fieldValue == null) {
|
||||
valueRowBuilder.addColumns(CommonConstants.NULL_VALUE);
|
||||
} else {
|
||||
valueRowBuilder.addColumns(fieldValue);
|
||||
}
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
|
||||
private void parseResponseDataByOne(String result, List<String> aliasFields, CollectRep.MetricsData.Builder builder, Long responseTime) {
|
||||
String[] lines = result.split("\n");
|
||||
if (lines.length + 1 < aliasFields.size()) {
|
||||
log.error("ssh response data not enough: {}", result);
|
||||
return;
|
||||
}
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
int aliasIndex = 0;
|
||||
int lineIndex = 0;
|
||||
while (aliasIndex < aliasFields.size()) {
|
||||
if (CollectorConstants.RESPONSE_TIME.equalsIgnoreCase(aliasFields.get(aliasIndex))) {
|
||||
valueRowBuilder.addColumns(responseTime.toString());
|
||||
} else {
|
||||
if (lineIndex < lines.length) {
|
||||
valueRowBuilder.addColumns(lines[lineIndex].trim());
|
||||
} else {
|
||||
valueRowBuilder.addColumns(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
lineIndex++;
|
||||
}
|
||||
aliasIndex++;
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
|
||||
private void parseResponseDataByMulti(String result, List<String> aliasFields,
|
||||
CollectRep.MetricsData.Builder builder, Long responseTime) {
|
||||
String[] lines = result.split("\n");
|
||||
if (lines.length <= 1) {
|
||||
log.error("ssh response data only has header: {}", result);
|
||||
return;
|
||||
}
|
||||
String[] fields = lines[0].split(" ");
|
||||
Map<String, Integer> fieldMapping = new HashMap<>(fields.length);
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
fieldMapping.put(fields[i].trim().toLowerCase(), i);
|
||||
}
|
||||
for (int i = 1; i < lines.length; i++) {
|
||||
String[] values = lines[i].split(" ");
|
||||
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (String alias : aliasFields) {
|
||||
if (CollectorConstants.RESPONSE_TIME.equalsIgnoreCase(alias)) {
|
||||
valueRowBuilder.addColumns(responseTime.toString());
|
||||
} else {
|
||||
Integer index = fieldMapping.get(alias.toLowerCase());
|
||||
if (index != null && index < values.length) {
|
||||
valueRowBuilder.addColumns(values[index]);
|
||||
} else {
|
||||
valueRowBuilder.addColumns(CommonConstants.NULL_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.addValues(valueRowBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private void removeConnectSessionCache(SshProtocol sshProtocol) {
|
||||
CacheIdentifier identifier = CacheIdentifier.builder()
|
||||
.ip(sshProtocol.getHost()).port(sshProtocol.getPort())
|
||||
.username(sshProtocol.getUsername()).password(sshProtocol.getPassword())
|
||||
.build();
|
||||
connectionCommonCache.removeCache(identifier);
|
||||
}
|
||||
|
||||
private ClientSession getConnectSession(SshProtocol sshProtocol, int timeout, boolean reuseConnection)
|
||||
throws IOException, GeneralSecurityException {
|
||||
CacheIdentifier identifier = CacheIdentifier.builder()
|
||||
.ip(sshProtocol.getHost()).port(sshProtocol.getPort())
|
||||
.username(sshProtocol.getUsername()).password(sshProtocol.getPassword())
|
||||
.build();
|
||||
ClientSession clientSession = null;
|
||||
if (reuseConnection) {
|
||||
Optional<SshConnect> cacheOption = connectionCommonCache.getCache(identifier, true);
|
||||
if (cacheOption.isPresent()) {
|
||||
clientSession = cacheOption.get().getConnection();
|
||||
try {
|
||||
if (clientSession == null || clientSession.isClosed() || clientSession.isClosing()) {
|
||||
clientSession = null;
|
||||
connectionCommonCache.removeCache(identifier);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn(e.getMessage());
|
||||
clientSession = null;
|
||||
connectionCommonCache.removeCache(identifier);
|
||||
}
|
||||
}
|
||||
if (clientSession != null) {
|
||||
return clientSession;
|
||||
}
|
||||
}
|
||||
SshClient sshClient = CommonSshClient.getSshClient();
|
||||
clientSession = sshClient.connect(sshProtocol.getUsername(), sshProtocol.getHost(), Integer.parseInt(sshProtocol.getPort()))
|
||||
.verify(timeout, TimeUnit.MILLISECONDS).getSession();
|
||||
if (StringUtils.hasText(sshProtocol.getPassword())) {
|
||||
clientSession.addPasswordIdentity(sshProtocol.getPassword());
|
||||
} else if (StringUtils.hasText(sshProtocol.getPrivateKey())) {
|
||||
var resourceKey = PrivateKeyUtils.writePrivateKey(sshProtocol.getHost(), sshProtocol.getPrivateKey());
|
||||
SecurityUtils.loadKeyPairIdentities(null, () -> resourceKey, new FileInputStream(resourceKey), null)
|
||||
.forEach(clientSession::addPublicKeyIdentity);
|
||||
} // else auth with localhost private public key certificates
|
||||
|
||||
// auth
|
||||
if (!clientSession.auth().verify(timeout, TimeUnit.MILLISECONDS).isSuccess()) {
|
||||
clientSession.close();
|
||||
throw new IllegalArgumentException("ssh auth failed.");
|
||||
}
|
||||
if (reuseConnection) {
|
||||
SshConnect sshConnect = new SshConnect(clientSession);
|
||||
connectionCommonCache.addCache(identifier, sshConnect);
|
||||
}
|
||||
return clientSession;
|
||||
}
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* 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.collector.config;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
/**
|
||||
* Collector Auto Configuration
|
||||
* @version 2.1
|
||||
*/
|
||||
@ComponentScan(basePackages = "org.apache.hertzbeat.collector")
|
||||
public class CollectorAutoConfiguration {
|
||||
}
|
||||
@@ -1,421 +0,0 @@
|
||||
/*
|
||||
* 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.collector.dispatch;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.jexl3.JexlExpression;
|
||||
import org.apache.hertzbeat.collector.collect.AbstractCollect;
|
||||
import org.apache.hertzbeat.collector.collect.prometheus.PrometheusAutoCollectImpl;
|
||||
import org.apache.hertzbeat.collector.collect.strategy.CollectStrategyFactory;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.Timeout;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.WheelTimerTask;
|
||||
import org.apache.hertzbeat.collector.dispatch.unit.UnitConvert;
|
||||
import org.apache.hertzbeat.collector.util.CollectUtil;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.apache.hertzbeat.common.util.JexlExpressionRunner;
|
||||
import org.apache.hertzbeat.common.util.Pair;
|
||||
|
||||
/**
|
||||
* metrics collection
|
||||
*/
|
||||
@Slf4j
|
||||
@Data
|
||||
public class MetricsCollect implements Runnable, Comparable<MetricsCollect> {
|
||||
/**
|
||||
* Scheduling alarm threshold time 100ms
|
||||
*/
|
||||
private static final long WARN_DISPATCH_TIME = 100;
|
||||
/**
|
||||
* collector identity
|
||||
*/
|
||||
protected String collectorIdentity;
|
||||
/**
|
||||
* Tenant ID
|
||||
*/
|
||||
protected long tenantId;
|
||||
/**
|
||||
* Monitor ID
|
||||
*/
|
||||
protected long monitorId;
|
||||
/**
|
||||
* Monitoring type name
|
||||
*/
|
||||
protected String app;
|
||||
/**
|
||||
* Metrics configuration
|
||||
*/
|
||||
protected Metrics metrics;
|
||||
/**
|
||||
* time wheel timeout
|
||||
*/
|
||||
protected Timeout timeout;
|
||||
/**
|
||||
* Task and Data Scheduling
|
||||
*/
|
||||
protected CollectDataDispatch collectDataDispatch;
|
||||
/**
|
||||
* task execution priority
|
||||
*/
|
||||
protected byte runPriority;
|
||||
/**
|
||||
* Periodic collection or one-time collection true-periodic false-one-time
|
||||
*/
|
||||
protected boolean isCyclic;
|
||||
/**
|
||||
* Time for creating collection task
|
||||
*/
|
||||
protected long newTime;
|
||||
/**
|
||||
* Start time of the collection task
|
||||
*/
|
||||
protected long startTime;
|
||||
|
||||
protected List<UnitConvert> unitConvertList;
|
||||
|
||||
public MetricsCollect(Metrics metrics, Timeout timeout,
|
||||
CollectDataDispatch collectDataDispatch,
|
||||
String collectorIdentity,
|
||||
List<UnitConvert> unitConvertList) {
|
||||
this.newTime = System.currentTimeMillis();
|
||||
this.timeout = timeout;
|
||||
this.metrics = metrics;
|
||||
this.collectorIdentity = collectorIdentity;
|
||||
WheelTimerTask timerJob = (WheelTimerTask) timeout.task();
|
||||
Job job = timerJob.getJob();
|
||||
this.monitorId = job.getMonitorId();
|
||||
this.tenantId = job.getTenantId();
|
||||
this.app = job.getApp();
|
||||
this.collectDataDispatch = collectDataDispatch;
|
||||
this.isCyclic = job.isCyclic();
|
||||
this.unitConvertList = unitConvertList;
|
||||
// Temporary one-time tasks are executed with high priority
|
||||
if (isCyclic) {
|
||||
runPriority = (byte) -1;
|
||||
} else {
|
||||
runPriority = (byte) 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.startTime = System.currentTimeMillis();
|
||||
setNewThreadName(monitorId, app, startTime, metrics);
|
||||
CollectRep.MetricsData.Builder response = CollectRep.MetricsData.newBuilder();
|
||||
response.setApp(app);
|
||||
response.setId(monitorId);
|
||||
response.setTenantId(tenantId);
|
||||
// for prometheus auto
|
||||
if (DispatchConstants.PROTOCOL_PROMETHEUS.equalsIgnoreCase(metrics.getProtocol())) {
|
||||
List<CollectRep.MetricsData> metricsData = PrometheusAutoCollectImpl
|
||||
.getInstance().collect(response, metrics);
|
||||
validateResponse(metricsData.stream().findFirst().orElse(null));
|
||||
collectDataDispatch.dispatchCollectData(timeout, metrics, metricsData);
|
||||
return;
|
||||
}
|
||||
response.setMetrics(metrics.getName());
|
||||
// According to the metrics collection protocol, application type, etc.,
|
||||
// dispatch to the real application metrics collection implementation class
|
||||
AbstractCollect abstractCollect = CollectStrategyFactory.invoke(metrics.getProtocol());
|
||||
if (abstractCollect == null) {
|
||||
log.error("[Dispatcher] - not support this: app: {}, metrics: {}, protocol: {}.",
|
||||
app, metrics.getName(), metrics.getProtocol());
|
||||
response.setCode(CollectRep.Code.FAIL);
|
||||
response.setMsg("not support " + app + ", "
|
||||
+ metrics.getName() + ", " + metrics.getProtocol());
|
||||
} else {
|
||||
try {
|
||||
abstractCollect.preCheck(metrics);
|
||||
abstractCollect.collect(response, monitorId, app, metrics);
|
||||
} catch (Exception e) {
|
||||
String msg = e.getMessage();
|
||||
if (msg == null && e.getCause() != null) {
|
||||
msg = e.getCause().getMessage();
|
||||
}
|
||||
if (e instanceof IllegalArgumentException){
|
||||
log.error("[Metrics PreCheck]: {}.", msg, e);
|
||||
} else {
|
||||
log.error("[Metrics Collect]: {}.", msg, e);
|
||||
}
|
||||
response.setCode(CollectRep.Code.FAIL);
|
||||
if (msg != null) {
|
||||
response.setMsg(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Alias attribute expression replacement calculation
|
||||
if (fastFailed()) {
|
||||
return;
|
||||
}
|
||||
calculateFields(metrics, response);
|
||||
CollectRep.MetricsData metricsData = validateResponse(response);
|
||||
collectDataDispatch.dispatchCollectData(timeout, metrics, metricsData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate the real metrics value according to the calculates and aliasFields configuration
|
||||
*
|
||||
* @param metrics Metrics configuration
|
||||
* @param collectData Data collection
|
||||
*/
|
||||
private void calculateFields(Metrics metrics, CollectRep.MetricsData.Builder collectData) {
|
||||
collectData.setPriority(metrics.getPriority());
|
||||
List<CollectRep.Field> fieldList = new LinkedList<>();
|
||||
for (Metrics.Field field : metrics.getFields()) {
|
||||
CollectRep.Field.Builder fieldBuilder = CollectRep.Field.newBuilder();
|
||||
fieldBuilder.setName(field.getField()).setType(field.getType()).setLabel(field.isLabel());
|
||||
if (field.getUnit() != null) {
|
||||
fieldBuilder.setUnit(field.getUnit());
|
||||
}
|
||||
fieldList.add(fieldBuilder.build());
|
||||
}
|
||||
collectData.addAllFields(fieldList);
|
||||
List<CollectRep.ValueRow> aliasRowList = collectData.getValuesList();
|
||||
if (aliasRowList == null || aliasRowList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
collectData.clearValues();
|
||||
// Preprocess calculates first
|
||||
if (metrics.getCalculates() == null) {
|
||||
metrics.setCalculates(Collections.emptyList());
|
||||
}
|
||||
// eg: database_pages=Database pages unconventional mapping
|
||||
Map<String, String> fieldAliasMap = new HashMap<>(8);
|
||||
Map<String, JexlExpression> fieldExpressionMap = metrics.getCalculates()
|
||||
.stream()
|
||||
.map(cal -> transformCal(cal, fieldAliasMap))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(arr -> (String) arr[0], arr -> (JexlExpression) arr[1], (oldValue, newValue) -> newValue));
|
||||
|
||||
if (metrics.getUnits() == null) {
|
||||
metrics.setUnits(Collections.emptyList());
|
||||
}
|
||||
Map<String, Pair<String, String>> fieldUnitMap = metrics.getUnits()
|
||||
.stream()
|
||||
.map(this::transformUnit)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(arr -> (String) arr[0], arr -> (Pair<String, String>) arr[1], (oldValue, newValue) -> newValue));
|
||||
|
||||
List<Metrics.Field> fields = metrics.getFields();
|
||||
List<String> aliasFields = metrics.getAliasFields();
|
||||
Map<String, String> aliasFieldValueMap = new HashMap<>(8);
|
||||
Map<String, Object> fieldValueMap = new HashMap<>(8);
|
||||
Map<String, String> aliasFieldUnitMap = new HashMap<>(8);
|
||||
CollectRep.ValueRow.Builder realValueRowBuilder = CollectRep.ValueRow.newBuilder();
|
||||
for (CollectRep.ValueRow aliasRow : aliasRowList) {
|
||||
for (int aliasIndex = 0; aliasIndex < aliasFields.size(); aliasIndex++) {
|
||||
String aliasFieldValue = aliasRow.getColumns(aliasIndex);
|
||||
String aliasField = aliasFields.get(aliasIndex);
|
||||
if (!CommonConstants.NULL_VALUE.equals(aliasFieldValue)) {
|
||||
aliasFieldValueMap.put(aliasField, aliasFieldValue);
|
||||
// whether the alias field is a number
|
||||
CollectUtil.DoubleAndUnit doubleAndUnit = CollectUtil
|
||||
.extractDoubleAndUnitFromStr(aliasFieldValue);
|
||||
if (doubleAndUnit != null && doubleAndUnit.getValue() != null) {
|
||||
fieldValueMap.put(aliasField, doubleAndUnit.getValue());
|
||||
if (doubleAndUnit.getUnit() != null) {
|
||||
aliasFieldUnitMap.put(aliasField, doubleAndUnit.getUnit());
|
||||
}
|
||||
} else {
|
||||
fieldValueMap.put(aliasField, aliasFieldValue);
|
||||
}
|
||||
} else {
|
||||
fieldValueMap.put(aliasField, null);
|
||||
}
|
||||
}
|
||||
|
||||
for (Metrics.Field field : fields) {
|
||||
String realField = field.getField();
|
||||
JexlExpression expression = fieldExpressionMap.get(realField);
|
||||
String value = null;
|
||||
String aliasFieldUnit = null;
|
||||
if (expression != null) {
|
||||
try {
|
||||
for (Map.Entry<String, String> unitEntry : aliasFieldUnitMap.entrySet()) {
|
||||
if (expression.getSourceText().contains(unitEntry.getKey())) {
|
||||
aliasFieldUnit = unitEntry.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Also executed when valueList is empty, covering pure string assignment expressions
|
||||
Object objValue = JexlExpressionRunner.evaluate(expression, fieldValueMap);
|
||||
if (objValue != null) {
|
||||
value = String.valueOf(objValue);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.info("[calculates execute warning] {}.", e.getMessage());
|
||||
}
|
||||
} else {
|
||||
// does not exist then map the alias value
|
||||
String aliasField = fieldAliasMap.get(realField);
|
||||
if (aliasField != null) {
|
||||
value = aliasFieldValueMap.get(aliasField);
|
||||
} else {
|
||||
value = aliasFieldValueMap.get(realField);
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
final byte fieldType = field.getType();
|
||||
if (fieldType == CommonConstants.TYPE_NUMBER) {
|
||||
CollectUtil.DoubleAndUnit doubleAndUnit = CollectUtil
|
||||
.extractDoubleAndUnitFromStr(value);
|
||||
final Double tempValue = doubleAndUnit == null ? null : doubleAndUnit.getValue();
|
||||
value = tempValue == null ? null : String.valueOf(tempValue);
|
||||
aliasFieldUnit = doubleAndUnit == null ? null : doubleAndUnit.getUnit();
|
||||
} else if (fieldType == CommonConstants.TYPE_TIME) {
|
||||
final int tempValue;
|
||||
value = (tempValue = CommonUtil.parseTimeStrToSecond(value)) == -1 ? null : String.valueOf(tempValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pair<String, String> unitPair = fieldUnitMap.get(realField);
|
||||
if (aliasFieldUnit != null) {
|
||||
if (unitPair != null) {
|
||||
unitPair.setLeft(aliasFieldUnit);
|
||||
} else if (field.getUnit() != null && !aliasFieldUnit.equalsIgnoreCase(field.getUnit())) {
|
||||
unitPair = Pair.of(aliasFieldUnit, field.getUnit());
|
||||
}
|
||||
}
|
||||
if (value != null && unitPair != null) {
|
||||
for (UnitConvert unitConvert : unitConvertList) {
|
||||
if (unitConvert.checkUnit(unitPair.getLeft()) && unitConvert.checkUnit(unitPair.getRight())) {
|
||||
value = unitConvert.convert(value, unitPair.getLeft(), unitPair.getRight());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle metrics values that may have units such as 34%, 34Mb, and limit values to 4 decimal places
|
||||
if (CommonConstants.TYPE_NUMBER == field.getType()) {
|
||||
value = CommonUtil.parseDoubleStr(value, field.getUnit());
|
||||
}
|
||||
// Handle the case where the value is string type and value is numeric
|
||||
if (CommonConstants.TYPE_STRING == field.getType() && CommonUtil.isNumeric(value)) {
|
||||
value = CommonUtil.parseDoubleStr(value, field.getUnit());
|
||||
}
|
||||
if (value == null) {
|
||||
value = CommonConstants.NULL_VALUE;
|
||||
}
|
||||
realValueRowBuilder.addColumns(value);
|
||||
}
|
||||
aliasFieldValueMap.clear();
|
||||
fieldValueMap.clear();
|
||||
aliasFieldUnitMap.clear();
|
||||
collectData.addValues(realValueRowBuilder.build());
|
||||
realValueRowBuilder.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param cal cal
|
||||
* @param fieldAliasMap field alias map
|
||||
* @return expr
|
||||
*/
|
||||
private Object[] transformCal(String cal, Map<String, String> fieldAliasMap) {
|
||||
int splitIndex = cal.indexOf("=");
|
||||
String field = cal.substring(0, splitIndex).trim();
|
||||
String expressionStr = cal.substring(splitIndex + 1).trim().replace("\\#", "#");
|
||||
JexlExpression expression;
|
||||
try {
|
||||
expression = JexlExpressionRunner.compile(expressionStr);
|
||||
} catch (Exception e) {
|
||||
fieldAliasMap.put(field, expressionStr);
|
||||
return null;
|
||||
}
|
||||
return new Object[]{field, expression};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* transform unit
|
||||
* @param unit unit
|
||||
* @return units
|
||||
*/
|
||||
private Object[] transformUnit(String unit) {
|
||||
int equalIndex = unit.indexOf("=");
|
||||
int arrowIndex = unit.indexOf("->");
|
||||
if (equalIndex < 0 || arrowIndex < 0) {
|
||||
return null;
|
||||
}
|
||||
String field = unit.substring(0, equalIndex).trim();
|
||||
String originUnit = unit.substring(equalIndex + 1, arrowIndex).trim();
|
||||
String newUnit = unit.substring(arrowIndex + 2).trim();
|
||||
return new Object[]{field, Pair.of(originUnit, newUnit)};
|
||||
}
|
||||
|
||||
private boolean fastFailed() {
|
||||
return this.timeout == null || this.timeout.isCancelled();
|
||||
}
|
||||
|
||||
private CollectRep.MetricsData validateResponse(CollectRep.MetricsData.Builder builder) {
|
||||
long endTime = System.currentTimeMillis();
|
||||
builder.setTime(endTime);
|
||||
long runningTime = endTime - startTime;
|
||||
long allTime = endTime - newTime;
|
||||
if (startTime - newTime >= WARN_DISPATCH_TIME) {
|
||||
log.warn("[Collector Dispatch Warn, Dispatch Use {}ms.", startTime - newTime);
|
||||
}
|
||||
if (builder.getCode() != CollectRep.Code.SUCCESS) {
|
||||
log.info("[Collect Failed, Run {}ms, All {}ms] Reason: {}", runningTime, allTime, builder.getMsg());
|
||||
} else {
|
||||
log.info("[Collect Success, Run {}ms, All {}ms].", runningTime, allTime);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void validateResponse(CollectRep.MetricsData metricsData) {
|
||||
if (metricsData == null) {
|
||||
log.error("[Collect Failed] Response metrics data is null.");
|
||||
return;
|
||||
}
|
||||
long endTime = System.currentTimeMillis();
|
||||
long runningTime = endTime - startTime;
|
||||
long allTime = endTime - newTime;
|
||||
if (startTime - newTime >= WARN_DISPATCH_TIME) {
|
||||
log.warn("[Collector Dispatch Warn, Dispatch Use {}ms.", startTime - newTime);
|
||||
}
|
||||
if (metricsData.getCode() != CollectRep.Code.SUCCESS) {
|
||||
log.info("[Collect Failed, Run {}ms, All {}ms] Reason: {}", runningTime, allTime, metricsData.getMsg());
|
||||
} else {
|
||||
log.info("[Collect Success, Run {}ms, All {}ms].", runningTime, allTime);
|
||||
}
|
||||
}
|
||||
|
||||
private void setNewThreadName(long monitorId, String app, long startTime, Metrics metrics) {
|
||||
String builder = monitorId + "-" + app + "-" + metrics.getName()
|
||||
+ "-" + String.valueOf(startTime).substring(9);
|
||||
Thread.currentThread().setName(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(MetricsCollect collect) {
|
||||
return collect.runPriority - this.runPriority;
|
||||
}
|
||||
}
|
||||
-176
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* 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.collector.dispatch.entrance.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.dispatch.DispatchProperties;
|
||||
import org.apache.hertzbeat.collector.dispatch.WorkerPool;
|
||||
import org.apache.hertzbeat.collector.dispatch.entrance.CollectServer;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.message.ClusterMsg;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.util.IpDomainUtil;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.hertzbeat.common.util.ProtoJsonUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Collection job management provides api interface
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class CollectJobService {
|
||||
|
||||
private static final String COLLECTOR_STR = "-collector";
|
||||
|
||||
private final TimerDispatch timerDispatch;
|
||||
|
||||
private final WorkerPool workerPool;
|
||||
|
||||
private final String collectorIdentity;
|
||||
|
||||
private String mode = null;
|
||||
|
||||
private CollectServer collectServer;
|
||||
|
||||
public CollectJobService(TimerDispatch timerDispatch, DispatchProperties properties, WorkerPool workerPool) {
|
||||
this.timerDispatch = timerDispatch;
|
||||
this.workerPool = workerPool;
|
||||
if (properties != null && properties.getEntrance() != null && properties.getEntrance().getNetty() != null
|
||||
&& properties.getEntrance().getNetty().isEnabled()) {
|
||||
mode = properties.getEntrance().getNetty().getMode();
|
||||
String collectorName = properties.getEntrance().getNetty().getIdentity();
|
||||
if (StringUtils.hasText(collectorName)) {
|
||||
collectorIdentity = collectorName;
|
||||
} else {
|
||||
collectorIdentity = IpDomainUtil.getCurrentHostName() + COLLECTOR_STR;
|
||||
log.info("user not config this collector identity, use [host name - host ip] default: {}.", collectorIdentity);
|
||||
}
|
||||
} else {
|
||||
collectorIdentity = CommonConstants.MAIN_COLLECTOR_NODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one-time collection task and get the collected data response
|
||||
*
|
||||
* @param job Collect task details
|
||||
* @return Collection results
|
||||
*/
|
||||
public List<CollectRep.MetricsData> collectSyncJobData(Job job) {
|
||||
final List<CollectRep.MetricsData> metricsData = new LinkedList<>();
|
||||
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
CollectResponseEventListener listener = new CollectResponseEventListener() {
|
||||
@Override
|
||||
public void response(List<CollectRep.MetricsData> responseMetrics) {
|
||||
if (responseMetrics != null) {
|
||||
metricsData.addAll(responseMetrics);
|
||||
}
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
};
|
||||
timerDispatch.addJob(job, listener);
|
||||
try {
|
||||
countDownLatch.await(120, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
log.info("The sync task runs for 120 seconds with no response and returns");
|
||||
}
|
||||
return metricsData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a one-time collection task and send the collected data response
|
||||
*
|
||||
* @param oneTimeJob Collect task details
|
||||
*/
|
||||
public void collectSyncOneTimeJobData(Job oneTimeJob) {
|
||||
workerPool.executeJob(() -> {
|
||||
List<CollectRep.MetricsData> metricsDataList = this.collectSyncJobData(oneTimeJob);
|
||||
List<String> jsons = new ArrayList<>(metricsDataList.size());
|
||||
for (CollectRep.MetricsData metricsData : metricsDataList) {
|
||||
String json = ProtoJsonUtil.toJsonStr(metricsData);
|
||||
if (json != null) {
|
||||
jsons.add(json);
|
||||
}
|
||||
}
|
||||
String response = JsonUtil.toJson(jsons);
|
||||
ClusterMsg.Message message = ClusterMsg.Message.newBuilder()
|
||||
.setMsg(response)
|
||||
.setDirection(ClusterMsg.Direction.REQUEST)
|
||||
.setType(ClusterMsg.MessageType.RESPONSE_ONE_TIME_TASK_DATA)
|
||||
.build();
|
||||
this.collectServer.sendMsg(message);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue periodic asynchronous collection tasks
|
||||
*
|
||||
* @param job Collect task details
|
||||
*/
|
||||
public void addAsyncCollectJob(Job job) {
|
||||
timerDispatch.addJob(job.clone(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel periodic asynchronous collection tasks
|
||||
*
|
||||
* @param jobId Job ID
|
||||
*/
|
||||
public void cancelAsyncCollectJob(Long jobId) {
|
||||
if (jobId != null) {
|
||||
timerDispatch.deleteJob(jobId, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* send async collect response data
|
||||
*
|
||||
* @param metricsData collect data
|
||||
*/
|
||||
public void sendAsyncCollectData(CollectRep.MetricsData metricsData) {
|
||||
String data = ProtoJsonUtil.toJsonStr(metricsData);
|
||||
ClusterMsg.Message message = ClusterMsg.Message.newBuilder()
|
||||
.setIdentity(collectorIdentity)
|
||||
.setMsg(data)
|
||||
.setDirection(ClusterMsg.Direction.REQUEST)
|
||||
.setType(ClusterMsg.MessageType.RESPONSE_CYCLIC_TASK_DATA)
|
||||
.build();
|
||||
this.collectServer.sendMsg(message);
|
||||
}
|
||||
|
||||
public String getCollectorIdentity() {
|
||||
return collectorIdentity;
|
||||
}
|
||||
|
||||
public String getCollectorMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public void setCollectServer(CollectServer collectServer) {
|
||||
this.collectServer = collectServer;
|
||||
}
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* 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.collector.dispatch.entrance.processor;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.dispatch.timer.TimerDispatch;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.message.ClusterMsg;
|
||||
import org.apache.hertzbeat.common.support.SpringContextHolder;
|
||||
import org.apache.hertzbeat.remoting.netty.NettyRemotingProcessor;
|
||||
|
||||
/**
|
||||
* handle collector online message
|
||||
* Note: This is not to re-open the connection with Manager, nor can it be done, just to re-open the collection function
|
||||
*/
|
||||
@Slf4j
|
||||
public class GoOnlineProcessor implements NettyRemotingProcessor {
|
||||
|
||||
private TimerDispatch timerDispatch;
|
||||
|
||||
@Override
|
||||
public ClusterMsg.Message handle(ChannelHandlerContext ctx, ClusterMsg.Message message) {
|
||||
if (this.timerDispatch == null) {
|
||||
this.timerDispatch = SpringContextHolder.getBean(TimerDispatch.class);
|
||||
}
|
||||
timerDispatch.goOnline();
|
||||
log.info("receive online message and handle success");
|
||||
return ClusterMsg.Message.newBuilder()
|
||||
.setIdentity(message.getIdentity())
|
||||
.setDirection(ClusterMsg.Direction.RESPONSE)
|
||||
.setMsg(String.valueOf(CommonConstants.SUCCESS_CODE))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* 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.collector.dispatch.export;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.dispatch.entrance.internal.CollectJobService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.queue.CommonDataQueue;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* for collector instance
|
||||
* send collect response data by netty
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@ConditionalOnProperty(
|
||||
prefix = NettyDataQueue.NETTY_DATA_QUEUE_PREFIX,
|
||||
name = "type",
|
||||
havingValue = "netty"
|
||||
)
|
||||
public class NettyDataQueue implements CommonDataQueue {
|
||||
|
||||
/**
|
||||
* netty data queue prefix.
|
||||
*/
|
||||
protected static final String NETTY_DATA_QUEUE_PREFIX = "common.queue";
|
||||
|
||||
private final CollectJobService collectJobService;
|
||||
|
||||
public NettyDataQueue(CollectJobService collectJobService) {
|
||||
this.collectJobService = collectJobService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendAlertsData(Alert alert) {}
|
||||
|
||||
@Override
|
||||
public Alert pollAlertsData() throws InterruptedException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectRep.MetricsData pollMetricsDataToAlerter() throws InterruptedException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectRep.MetricsData pollMetricsDataToPersistentStorage() throws InterruptedException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectRep.MetricsData pollMetricsDataToRealTimeStorage() throws InterruptedException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMetricsData(CollectRep.MetricsData metricsData) {
|
||||
collectJobService.sendAsyncCollectData(metricsData);
|
||||
}
|
||||
}
|
||||
-152
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* 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.collector.dispatch.timer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.dispatch.entrance.internal.CollectResponseEventListener;
|
||||
import org.apache.hertzbeat.common.entity.job.Job;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* job timer dispatcher
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class TimerDispatcher implements TimerDispatch, DisposableBean {
|
||||
|
||||
/**
|
||||
* time round schedule
|
||||
*/
|
||||
private final Timer wheelTimer;
|
||||
/**
|
||||
* Existing periodic scheduled tasks
|
||||
*/
|
||||
private final Map<Long, Timeout> currentCyclicTaskMap;
|
||||
/**
|
||||
* Existing temporary scheduled tasks
|
||||
*/
|
||||
private final Map<Long, Timeout> currentTempTaskMap;
|
||||
/**
|
||||
* One-time task response listener holds
|
||||
* jobId - listener
|
||||
*/
|
||||
private final Map<Long, CollectResponseEventListener> eventListeners;
|
||||
|
||||
/**
|
||||
* is dispatcher online running
|
||||
*/
|
||||
private final AtomicBoolean started;
|
||||
|
||||
public TimerDispatcher() {
|
||||
this.wheelTimer = new HashedWheelTimer(r -> {
|
||||
Thread ret = new Thread(r, "wheelTimer");
|
||||
ret.setDaemon(true);
|
||||
return ret;
|
||||
}, 1, TimeUnit.SECONDS, 512);
|
||||
this.currentCyclicTaskMap = new ConcurrentHashMap<>(8);
|
||||
this.currentTempTaskMap = new ConcurrentHashMap<>(8);
|
||||
this.eventListeners = new ConcurrentHashMap<>(8);
|
||||
this.started = new AtomicBoolean(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addJob(Job addJob, CollectResponseEventListener eventListener) {
|
||||
if (!this.started.get()) {
|
||||
log.warn("Collector is offline, can not dispatch collect jobs.");
|
||||
return;
|
||||
}
|
||||
WheelTimerTask timerJob = new WheelTimerTask(addJob);
|
||||
if (addJob.isCyclic()) {
|
||||
Timeout timeout = wheelTimer.newTimeout(timerJob, addJob.getInterval(), TimeUnit.SECONDS);
|
||||
currentCyclicTaskMap.put(addJob.getId(), timeout);
|
||||
} else {
|
||||
Timeout timeout = wheelTimer.newTimeout(timerJob, 0, TimeUnit.SECONDS);
|
||||
currentTempTaskMap.put(addJob.getId(), timeout);
|
||||
eventListeners.put(addJob.getId(), eventListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cyclicJob(WheelTimerTask timerTask, long interval, TimeUnit timeUnit) {
|
||||
if (!this.started.get()) {
|
||||
log.warn("Collector is offline, can not dispatch collect jobs.");
|
||||
return;
|
||||
}
|
||||
Long jobId = timerTask.getJob().getId();
|
||||
// whether is the job has been canceled
|
||||
if (currentCyclicTaskMap.containsKey(jobId)) {
|
||||
Timeout timeout = wheelTimer.newTimeout(timerTask, interval, TimeUnit.SECONDS);
|
||||
currentCyclicTaskMap.put(timerTask.getJob().getId(), timeout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteJob(long jobId, boolean isCyclic) {
|
||||
if (isCyclic) {
|
||||
Timeout timeout = currentCyclicTaskMap.remove(jobId);
|
||||
if (timeout != null) {
|
||||
timeout.cancel();
|
||||
}
|
||||
} else {
|
||||
Timeout timeout = currentTempTaskMap.remove(jobId);
|
||||
if (timeout != null) {
|
||||
timeout.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goOnline() {
|
||||
currentCyclicTaskMap.forEach((key, value) -> value.cancel());
|
||||
currentCyclicTaskMap.clear();
|
||||
currentTempTaskMap.forEach((key, value) -> value.cancel());
|
||||
currentTempTaskMap.clear();
|
||||
started.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goOffline() {
|
||||
started.set(false);
|
||||
currentCyclicTaskMap.forEach((key, value) -> value.cancel());
|
||||
currentCyclicTaskMap.clear();
|
||||
currentTempTaskMap.forEach((key, value) -> value.cancel());
|
||||
currentTempTaskMap.clear();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void responseSyncJobData(long jobId, List<CollectRep.MetricsData> metricsDataTemps) {
|
||||
currentTempTaskMap.remove(jobId);
|
||||
CollectResponseEventListener eventListener = eventListeners.remove(jobId);
|
||||
if (eventListener != null) {
|
||||
eventListener.response(metricsDataTemps);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
this.wheelTimer.stop();
|
||||
}
|
||||
}
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* 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.collector.dispatch.unit.impl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.collector.dispatch.unit.DataUnit;
|
||||
import org.apache.hertzbeat.collector.dispatch.unit.UnitConvert;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* the convert of data size
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public final class DataSizeConvert implements UnitConvert {
|
||||
|
||||
@Override
|
||||
public String convert(String value, String originUnit, String newUnit) {
|
||||
if (value == null || value.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (!checkUnit(originUnit) || !checkUnit(newUnit)) {
|
||||
return null;
|
||||
}
|
||||
if (originUnit.equalsIgnoreCase(newUnit)) {
|
||||
log.warn("The origin unit is the same as the new unit, no need to convert");
|
||||
return value;
|
||||
}
|
||||
BigDecimal size = new BigDecimal(value);
|
||||
// Idea: Value is converted to bytes through origin unit,
|
||||
// and then converted to the value corresponding to the new unit unit
|
||||
for (DataUnit dataUnit : DataUnit.values()) {
|
||||
if (dataUnit.getUnit().equals(originUnit.toUpperCase())) {
|
||||
size = size.multiply(new BigDecimal(dataUnit.getScale()));
|
||||
}
|
||||
if (dataUnit.getUnit().equals(newUnit.toUpperCase())) {
|
||||
size = size.divide(new BigDecimal(dataUnit.getScale()), 12, RoundingMode.HALF_UP);
|
||||
}
|
||||
}
|
||||
return size.setScale(4, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkUnit(String unit) {
|
||||
if (unit == null || unit.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (DataUnit dataUnit : DataUnit.values()) {
|
||||
// not case-sensitive
|
||||
if (dataUnit.getUnit().equals(unit.toUpperCase())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* 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.collector.dispatch.unit.impl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import org.apache.hertzbeat.collector.dispatch.unit.TimeLengthUnit;
|
||||
import org.apache.hertzbeat.collector.dispatch.unit.UnitConvert;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* the convert of time length
|
||||
*/
|
||||
@Component
|
||||
public final class TimeLengthConvert implements UnitConvert {
|
||||
|
||||
|
||||
@Override
|
||||
public String convert(String value, String originUnit, String newUnit) {
|
||||
if (value == null || value.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
BigDecimal length = new BigDecimal(value);
|
||||
// Idea: Value is converted to nanoseconds through origin unit,
|
||||
// and then converted to the value corresponding to the new unit unit
|
||||
for (TimeLengthUnit timeLengthUnit : TimeLengthUnit.values()) {
|
||||
if (timeLengthUnit.getUnit().equals(originUnit.toUpperCase())) {
|
||||
length = length.multiply(new BigDecimal(timeLengthUnit.getScale()));
|
||||
}
|
||||
if (timeLengthUnit.getUnit().equals(newUnit.toUpperCase())) {
|
||||
length = length.divide(new BigDecimal(timeLengthUnit.getScale()), 12, RoundingMode.HALF_UP);
|
||||
}
|
||||
}
|
||||
return length.setScale(4, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkUnit(String unit) {
|
||||
if (unit == null || unit.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (TimeLengthUnit timeUnit : TimeLengthUnit.values()) {
|
||||
// not case-sensitive
|
||||
if (timeUnit.getUnit().equals(unit.toUpperCase())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,458 +0,0 @@
|
||||
/*
|
||||
* 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.collector.util;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.job.Configmap;
|
||||
import org.apache.hertzbeat.common.entity.job.Metrics;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
|
||||
/**
|
||||
* util for collector
|
||||
*/
|
||||
@Slf4j
|
||||
public final class CollectUtil {
|
||||
|
||||
private static final int DEFAULT_TIMEOUT = 60000;
|
||||
private static final int HEX_STR_WIDTH = 2;
|
||||
private static final String SMILING_PLACEHOLDER = "^_^";
|
||||
private static final String SMILING_PLACEHOLDER_REX = "\\^_\\^";
|
||||
private static final String SMILING_PLACEHOLDER_REGEX = "(\\^_\\^)(\\w|-|$|\\.)+(\\^_\\^)";
|
||||
private static final Pattern SMILING_PLACEHOLDER_REGEX_PATTERN = Pattern.compile(SMILING_PLACEHOLDER_REGEX);
|
||||
private static final String CRYING_PLACEHOLDER_REX = "\\^o\\^";
|
||||
private static final String CRYING_PLACEHOLDER_REGEX = "(\\^o\\^)(\\w|-|$|\\.)+(\\^o\\^)";
|
||||
private static final Pattern CRYING_PLACEHOLDER_REGEX_PATTERN = Pattern.compile(CRYING_PLACEHOLDER_REGEX);
|
||||
private static final List<String> UNIT_SYMBOLS = Arrays.asList("%", "G", "g", "M", "m", "K", "k", "B", "b");
|
||||
|
||||
/**
|
||||
* private constructor, not allow to create instance.
|
||||
*/
|
||||
private CollectUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Regularly verifying whether a string is a combination of numbers and units
|
||||
*/
|
||||
private static final String DOUBLE_AND_UNIT_CHECK_REGEX = "^[.\\d+" + String.join("", UNIT_SYMBOLS) + "]+$";
|
||||
|
||||
/**
|
||||
* count match keyword number
|
||||
* @param content content
|
||||
* @param keyword keyword
|
||||
* @return match num
|
||||
*/
|
||||
public static int countMatchKeyword(String content, String keyword) {
|
||||
if (StringUtils.isAnyEmpty(content, keyword)) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
Pattern pattern = Pattern.compile(keyword);
|
||||
Matcher matcher = pattern.matcher(content);
|
||||
int count = 0;
|
||||
while (matcher.find()) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static DoubleAndUnit extractDoubleAndUnitFromStr(String str) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return null;
|
||||
}
|
||||
DoubleAndUnit doubleAndUnit = new DoubleAndUnit();
|
||||
try {
|
||||
Double doubleValue = Double.parseDouble(str);
|
||||
doubleAndUnit.setValue(doubleValue);
|
||||
return doubleAndUnit;
|
||||
} catch (Exception e) {
|
||||
log.debug(e.getMessage());
|
||||
}
|
||||
|
||||
if (!str.matches(DOUBLE_AND_UNIT_CHECK_REGEX)){
|
||||
return doubleAndUnit;
|
||||
}
|
||||
// extract unit from str value, eg: 23.43GB, 33KB, 44.22G
|
||||
try {
|
||||
// B KB MB GB % ....
|
||||
for (String unitSymbol : UNIT_SYMBOLS) {
|
||||
int index = str.indexOf(unitSymbol);
|
||||
if (index == 0) {
|
||||
Double doubleValue = 0d;
|
||||
String unit = str.trim();
|
||||
doubleAndUnit.setValue(doubleValue);
|
||||
doubleAndUnit.setUnit(unit);
|
||||
return doubleAndUnit;
|
||||
}
|
||||
if (index > 0) {
|
||||
Double doubleValue = Double.parseDouble(str.substring(0, index));
|
||||
String unit = str.substring(index).trim();
|
||||
doubleAndUnit.setValue(doubleValue);
|
||||
doubleAndUnit.setUnit(unit);
|
||||
return doubleAndUnit;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* double and unit
|
||||
*/
|
||||
public static final class DoubleAndUnit {
|
||||
|
||||
private Double value;
|
||||
private String unit;
|
||||
|
||||
public Double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public void setUnit(String unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get timeout integer
|
||||
*
|
||||
* @param timeout timeout str
|
||||
* @return timeout
|
||||
*/
|
||||
public static int getTimeout(String timeout) {
|
||||
return getTimeout(timeout, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* get timeout integer or default value
|
||||
*
|
||||
* @param timeout timeout str
|
||||
* @param defaultTimeout default timeout
|
||||
* @return timeout
|
||||
*/
|
||||
public static int getTimeout(String timeout, int defaultTimeout) {
|
||||
if (StringUtils.isEmpty(timeout)) {
|
||||
return defaultTimeout;
|
||||
}
|
||||
try {
|
||||
return Double.valueOf(timeout).intValue();
|
||||
} catch (Exception e) {
|
||||
return defaultTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* assert prom field
|
||||
*/
|
||||
public static Boolean assertPromRequireField(String aliasField) {
|
||||
return CommonConstants.PROM_TIME.equals(aliasField) || CommonConstants.PROM_VALUE.equals(aliasField);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* is contains cryPlaceholder ^o^xxx^o^
|
||||
*
|
||||
* @param jsonElement json element
|
||||
* @return return true when contains
|
||||
*/
|
||||
public static boolean containCryPlaceholder(JsonElement jsonElement) {
|
||||
String jsonStr = jsonElement.toString();
|
||||
return CRYING_PLACEHOLDER_REGEX_PATTERN.matcher(jsonStr).find();
|
||||
}
|
||||
|
||||
public static boolean notContainCryPlaceholder(JsonElement jsonElement) {
|
||||
return !containCryPlaceholder(jsonElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* match existed cry placeholder fields ^o^field^o^
|
||||
* @param jsonElement json element
|
||||
* @return match field str
|
||||
*/
|
||||
public static Set<String> matchCryPlaceholderField(JsonElement jsonElement) {
|
||||
String jsonStr = jsonElement.toString();
|
||||
return CRYING_PLACEHOLDER_REGEX_PATTERN.matcher(jsonStr).results()
|
||||
.map(item -> item.group().replaceAll(CRYING_PLACEHOLDER_REX, ""))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* json parameter replacement
|
||||
*
|
||||
* @param jsonElement json
|
||||
* @param configmap parameter map
|
||||
* @return json
|
||||
*/
|
||||
public static JsonElement replaceCryPlaceholder(JsonElement jsonElement, Map<String, Configmap> configmap) {
|
||||
if (jsonElement.isJsonObject()) {
|
||||
JsonObject jsonObject = jsonElement.getAsJsonObject();
|
||||
Iterator<Map.Entry<String, JsonElement>> iterator = jsonObject.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, JsonElement> entry = iterator.next();
|
||||
JsonElement element = entry.getValue();
|
||||
// Replace normal VALUE value
|
||||
if (element.isJsonPrimitive()) {
|
||||
// Check if there are special characters Replace
|
||||
String value = element.getAsString();
|
||||
Matcher cryingMatcher = CRYING_PLACEHOLDER_REGEX_PATTERN.matcher(value);
|
||||
if (cryingMatcher.find()) {
|
||||
cryingMatcher.reset();
|
||||
while (cryingMatcher.find()) {
|
||||
String group = cryingMatcher.group();
|
||||
String replaceField = group.replaceAll(CRYING_PLACEHOLDER_REX, "");
|
||||
Configmap param = configmap.get(replaceField);
|
||||
if (param != null) {
|
||||
if (param.getValue() == null) {
|
||||
if (group.length() == value.length()) {
|
||||
value = null;
|
||||
break;
|
||||
} else {
|
||||
value = value.replace(group, "");
|
||||
}
|
||||
} else {
|
||||
value = value.replace(group, (String) param.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
jsonObject.addProperty(entry.getKey(), value);
|
||||
}
|
||||
} else {
|
||||
jsonObject.add(entry.getKey(), replaceCryPlaceholder(entry.getValue(), configmap));
|
||||
}
|
||||
}
|
||||
} else if (jsonElement.isJsonArray()) {
|
||||
JsonArray jsonArray = jsonElement.getAsJsonArray();
|
||||
Iterator<JsonElement> iterator = jsonArray.iterator();
|
||||
int index = 0;
|
||||
while (iterator.hasNext()) {
|
||||
JsonElement element = iterator.next();
|
||||
if (element.isJsonPrimitive()) {
|
||||
// Check if there are special characters Replace
|
||||
String value = element.getAsString();
|
||||
Matcher cryingMatcher = CRYING_PLACEHOLDER_REGEX_PATTERN.matcher(value);
|
||||
if (cryingMatcher.find()) {
|
||||
cryingMatcher.reset();
|
||||
while (cryingMatcher.find()) {
|
||||
String group = cryingMatcher.group();
|
||||
String replaceField = group.replaceAll(CRYING_PLACEHOLDER_REX, "");
|
||||
Configmap param = configmap.get(replaceField);
|
||||
if (param != null) {
|
||||
if (param.getValue() == null) {
|
||||
if (group.length() == value.length()) {
|
||||
value = null;
|
||||
break;
|
||||
} else {
|
||||
value = value.replace(group, "");
|
||||
}
|
||||
} else {
|
||||
value = value.replace(group, (String) param.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
jsonArray.set(index, value == null ? JsonNull.INSTANCE : new JsonPrimitive(value));
|
||||
}
|
||||
} else {
|
||||
jsonArray.set(index, replaceCryPlaceholder(element, configmap));
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return jsonElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* json parameter replacement
|
||||
*
|
||||
* @param jsonElement json
|
||||
* @param configmap parameter map
|
||||
* @return json
|
||||
*/
|
||||
public static JsonElement replaceSmilingPlaceholder(JsonElement jsonElement, Map<String, Configmap> configmap) {
|
||||
if (jsonElement.isJsonObject()) {
|
||||
JsonObject jsonObject = jsonElement.getAsJsonObject();
|
||||
Iterator<Map.Entry<String, JsonElement>> iterator = jsonObject.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, JsonElement> entry = iterator.next();
|
||||
JsonElement element = entry.getValue();
|
||||
String key = entry.getKey();
|
||||
// Replace the attributes of the KEY-VALUE case such as http headers params
|
||||
if (key != null && key.startsWith(SMILING_PLACEHOLDER) && key.endsWith(SMILING_PLACEHOLDER)) {
|
||||
key = key.replaceAll(SMILING_PLACEHOLDER_REX, "");
|
||||
Configmap param = configmap.get(key);
|
||||
if (param != null && param.getType() == CommonConstants.PARAM_TYPE_MAP) {
|
||||
String jsonValue = (String) param.getValue();
|
||||
TypeReference<Map<String, String>> typeReference = new TypeReference<>() {
|
||||
};
|
||||
Map<String, String> map = JsonUtil.fromJson(jsonValue, typeReference);
|
||||
if (map != null) {
|
||||
map.forEach((name, value) -> {
|
||||
if (!StringUtils.isEmpty(name)) {
|
||||
jsonObject.addProperty(name, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
// Replace normal VALUE value
|
||||
if (element.isJsonPrimitive()) {
|
||||
// Check if there are special characters Replace
|
||||
String value = element.getAsString();
|
||||
Matcher smilingMatcher = SMILING_PLACEHOLDER_REGEX_PATTERN.matcher(value);
|
||||
if (smilingMatcher.find()) {
|
||||
smilingMatcher.reset();
|
||||
while (smilingMatcher.find()) {
|
||||
String group = smilingMatcher.group();
|
||||
String replaceField = group.replaceAll(SMILING_PLACEHOLDER_REX, "");
|
||||
Configmap param = configmap.get(replaceField);
|
||||
if (param != null) {
|
||||
if (param.getValue() == null) {
|
||||
if (group.length() == value.length()) {
|
||||
value = null;
|
||||
break;
|
||||
} else {
|
||||
value = value.replace(group, "");
|
||||
}
|
||||
} else {
|
||||
value = value.replace(group, (String) param.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
jsonObject.addProperty(entry.getKey(), value);
|
||||
}
|
||||
} else {
|
||||
jsonObject.add(entry.getKey(), replaceSmilingPlaceholder(entry.getValue(), configmap));
|
||||
}
|
||||
}
|
||||
} else if (jsonElement.isJsonArray()) {
|
||||
JsonArray jsonArray = jsonElement.getAsJsonArray();
|
||||
int index = 0;
|
||||
while (index < jsonArray.size()) {
|
||||
JsonElement element = jsonArray.get(index);
|
||||
if (element.isJsonPrimitive()) {
|
||||
// Check if there are special characters Replace
|
||||
String value = element.getAsString();
|
||||
Matcher smilingMatcher = SMILING_PLACEHOLDER_REGEX_PATTERN.matcher(value);
|
||||
if (smilingMatcher.find()) {
|
||||
smilingMatcher.reset();
|
||||
String[] arrayValues = null;
|
||||
while (smilingMatcher.find()) {
|
||||
String group = smilingMatcher.group();
|
||||
String replaceField = group.replaceAll(SMILING_PLACEHOLDER_REX, "");
|
||||
Configmap param = configmap.get(replaceField);
|
||||
if (param != null) {
|
||||
if (param.getValue() == null) {
|
||||
if (group.length() == value.length()) {
|
||||
value = null;
|
||||
break;
|
||||
} else {
|
||||
value = value.replace(group, "");
|
||||
}
|
||||
} else if (param.getType() == CommonConstants.PARAM_TYPE_ARRAY) {
|
||||
arrayValues = String.valueOf(param.getValue()).split(",");
|
||||
} else {
|
||||
value = value.replace(group, (String) param.getValue());
|
||||
}
|
||||
} else {
|
||||
value = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (arrayValues != null) {
|
||||
jsonArray.remove(index);
|
||||
index--;
|
||||
for (String arrayValue : arrayValues) {
|
||||
jsonArray.add(arrayValue);
|
||||
index++;
|
||||
}
|
||||
} else {
|
||||
jsonArray.set(index, value == null ? JsonNull.INSTANCE : new JsonPrimitive(value));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
jsonArray.set(index, replaceSmilingPlaceholder(element, configmap));
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return jsonElement;
|
||||
}
|
||||
|
||||
public static String replaceUriSpecialChar(String uri) {
|
||||
uri = uri.replaceAll(" ", "%20");
|
||||
// todo more special
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
||||
public static void replaceFieldsForPushStyleMonitor(Metrics metrics, Map<String, Configmap> configmap) {
|
||||
|
||||
List<Metrics.Field> pushFieldList = JsonUtil.fromJson((String) configmap.get("fields").getValue(), new TypeReference<>() {
|
||||
});
|
||||
metrics.setFields(pushFieldList);
|
||||
}
|
||||
|
||||
/**
|
||||
* convert 16 hexString to byte[]
|
||||
* eg: 302c0201010409636f6d6d756e697479a11c020419e502e7020100020100300e300c06082b060102010102000500
|
||||
* 16进制字符串不区分大小写,返回的数组相同
|
||||
* @param hexString 16 hexString
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] fromHexString(String hexString) {
|
||||
if (StringUtils.isEmpty(hexString)) {
|
||||
return null;
|
||||
}
|
||||
byte[] bytes = new byte[hexString.length() / HEX_STR_WIDTH];
|
||||
String hex;
|
||||
for (int i = 0; i < hexString.length() / HEX_STR_WIDTH; i++) {
|
||||
hex = hexString.substring(i * HEX_STR_WIDTH, i * HEX_STR_WIDTH + HEX_STR_WIDTH);
|
||||
bytes[i] = (byte) Integer.parseInt(hex, 16);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
org.apache.hertzbeat.collector.collect.http.HttpCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.http.SslCertificateCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.database.JdbcCommonCollect
|
||||
org.apache.hertzbeat.collector.collect.icmp.IcmpCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.jmx.JmxCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.redis.RedisCommonCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.mongodb.MongodbSingleCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.snmp.SnmpCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.ssh.SshCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.telnet.TelnetCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.smtp.SmtpCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.ntp.NtpCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.websocket.WebsocketCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.ftp.FtpCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.mq.RocketmqSingleCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.udp.UdpCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.push.PushCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.dns.DnsCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.nginx.NginxCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.memcached.MemcachedCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.nebulagraph.NebulaGraphCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.pop3.Pop3CollectImpl
|
||||
org.apache.hertzbeat.collector.collect.httpsd.HttpsdImpl
|
||||
org.apache.hertzbeat.collector.collect.redfish.RedfishCollectImpl
|
||||
org.apache.hertzbeat.collector.collect.nebulagraph.NgqlCollectImpl
|
||||
@@ -1,17 +0,0 @@
|
||||
# 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.
|
||||
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.apache.hertzbeat.collector.config.CollectorAutoConfiguration
|
||||
@@ -1,58 +0,0 @@
|
||||
# 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.
|
||||
server:
|
||||
port: 1159
|
||||
shutdown: graceful
|
||||
tomcat:
|
||||
threads:
|
||||
min-spare: 1
|
||||
spring:
|
||||
application:
|
||||
name: ${HOSTNAME:@hertzbeat-collector@}${PID}
|
||||
profiles:
|
||||
active: cluster
|
||||
lifecycle:
|
||||
timeout-per-shutdown-phase: 10s
|
||||
jackson:
|
||||
default-property-inclusion: ALWAYS
|
||||
# need to disable spring boot mongodb auto config, or default mongodb connection tried and failed...
|
||||
autoconfigure:
|
||||
exclude: org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration, org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
|
||||
|
||||
---
|
||||
spring:
|
||||
config:
|
||||
activate:
|
||||
on-profile: cluster
|
||||
|
||||
collector:
|
||||
dispatch:
|
||||
entrance:
|
||||
netty:
|
||||
enabled: true
|
||||
# Config the collector unique identity name.
|
||||
identity: ${IDENTITY:}
|
||||
# Config the running mode(public or private), public cluster or private cloud-edge.
|
||||
mode: ${MODE:public}
|
||||
manager-host: ${MANAGER_HOST:}${MANAGER_IP:}
|
||||
manager-port: ${MANAGER_PORT:1158}
|
||||
|
||||
push:
|
||||
uri: "127.0.0.1:1157"
|
||||
|
||||
|
||||
common:
|
||||
queue:
|
||||
type: netty
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user