mirror of
https://github.com/dromara/hertzbeat.git
synced 2026-09-17 18:19:02 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ad532ce75 | ||
|
|
bf9d1f2b1d | ||
|
|
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 |
@@ -2162,6 +2162,142 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "lctking",
|
||||
"name": "nullwli",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/168249998?v=4",
|
||||
"profile": "https://github.com/lctking",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "simonsigre",
|
||||
"name": "Simon Sigré",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/14932913?v=4",
|
||||
"profile": "https://simonsigre.com/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ponfee",
|
||||
"name": "ponfee",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/46117331?v=4",
|
||||
"profile": "http://www.ponfee.cn/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Vedant7789",
|
||||
"name": "Vedant7789",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/147625492?v=4",
|
||||
"profile": "https://github.com/Vedant7789",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Craaaaazy77",
|
||||
"name": "Craaaaazy77",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/23025522?v=4",
|
||||
"profile": "https://github.com/Craaaaazy77",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Suvrat1629",
|
||||
"name": "Suvrat1629",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/140749446?v=4",
|
||||
"profile": "https://github.com/Suvrat1629",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ghyghoo8",
|
||||
"name": "ghy",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/363129?v=4",
|
||||
"profile": "http://ghyghoo8.github.io/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "helei1030",
|
||||
"name": "helei1030",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/11839080?v=4",
|
||||
"profile": "https://github.com/helei1030",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "pjfanning",
|
||||
"name": "PJ Fanning",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/11783444?v=4",
|
||||
"profile": "https://medium.com/@pjfanning",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MonsterChenzhuo",
|
||||
"name": "monster",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/60029759?v=4",
|
||||
"profile": "https://github.com/MonsterChenzhuo",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MasamiYui",
|
||||
"name": "Sherlock Yin",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/22274133?v=4",
|
||||
"profile": "https://github.com/MasamiYui",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "wanhao23",
|
||||
"name": "wanhao",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/29560961?v=4",
|
||||
"profile": "https://github.com/wanhao23",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jonasHanhan",
|
||||
"name": "jonasHanhan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/130035609?v=4",
|
||||
"profile": "https://github.com/jonasHanhan",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "NikhilMurugesan",
|
||||
"name": "NikhilMurugesan",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/49281792?v=4",
|
||||
"profile": "https://github.com/NikhilMurugesan",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "myangle1120",
|
||||
"name": "myangle1120",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/19237013?v=4",
|
||||
"profile": "https://github.com/myangle1120",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
|
||||
# upload application logs
|
||||
- name: Upload logs & API test reports
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: hz-logs-${{ github.run_id }}
|
||||
|
||||
@@ -39,7 +39,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,7 +47,7 @@ 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
|
||||
|
||||
@@ -487,6 +487,27 @@ Thanks to these wonderful people, welcome to join us:
|
||||
<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></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></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></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></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>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -486,6 +486,27 @@ Thanks these wonderful people, welcome to join us:
|
||||
<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></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></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></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></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>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
"type": 0,
|
||||
"paramValue": 1000
|
||||
},
|
||||
{
|
||||
"field": "ssl",
|
||||
"type": 1,
|
||||
"paramValue": false
|
||||
},
|
||||
{
|
||||
"field": "username",
|
||||
"type": 1
|
||||
|
||||
+5
-5
@@ -302,13 +302,13 @@ items:
|
||||
Authorization: Bearer {{.login.data.token}}
|
||||
- name: deleteAlertDefine
|
||||
request:
|
||||
api: /api/alert/defines?ids={{(index .listAlertDefines.data.content 0).id | int64}}
|
||||
api: /api/alert/defines?ids=1
|
||||
method: DELETE
|
||||
header:
|
||||
Authorization: Bearer {{.login.data.token}}
|
||||
- name: listAlertConverges
|
||||
- name: listAlertGroupConverges
|
||||
request:
|
||||
api: /api/alert/converges
|
||||
api: /api/alert/groups
|
||||
header:
|
||||
Authorization: Bearer {{.login.data.token}}
|
||||
- name: listAlertSilences
|
||||
@@ -316,9 +316,9 @@ items:
|
||||
api: /api/alert/silences
|
||||
header:
|
||||
Authorization: Bearer {{.login.data.token}}
|
||||
- name: listAlerts
|
||||
- name: listGroupAlerts
|
||||
request:
|
||||
api: /api/alerts
|
||||
api: /api/alerts/group
|
||||
header:
|
||||
Authorization: Bearer {{.login.data.token}}
|
||||
- name: listReceivers
|
||||
|
||||
@@ -32,9 +32,19 @@
|
||||
<!-- common -->
|
||||
<dependency>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat-common</artifactId>
|
||||
<artifactId>hertzbeat-base</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- plugin -->
|
||||
<dependency>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat-plugin</artifactId>
|
||||
</dependency>
|
||||
<!-- warehouse -->
|
||||
<dependency>
|
||||
<groupId>org.apache.hertzbeat</groupId>
|
||||
<artifactId>hertzbeat-warehouse</artifactId>
|
||||
</dependency>
|
||||
<!-- spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -50,6 +60,21 @@
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- email -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
<!-- freemarker -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.nekohtml</groupId>
|
||||
<artifactId>nekohtml</artifactId>
|
||||
<version>${nekohtml.version}</version>
|
||||
</dependency>
|
||||
<!-- kafka -->
|
||||
<dependency>
|
||||
<groupId>org.apache.kafka</groupId>
|
||||
@@ -73,6 +98,36 @@
|
||||
<version>${easy-poi.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- sms -->
|
||||
<dependency>
|
||||
<groupId>com.tencentcloudapi</groupId>
|
||||
<artifactId>tencentcloud-sdk-java-sms</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>logging-interceptor</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.squareup.okio</groupId>
|
||||
<artifactId>okio</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.huaweicloud.sdk</groupId>
|
||||
<artifactId>huaweicloud-sdk-smn</artifactId>
|
||||
<version>${huawei.sdk.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.openeuler</groupId>
|
||||
<artifactId>bgmprovider</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.arrow</groupId>
|
||||
<artifactId>arrow-vector</artifactId>
|
||||
|
||||
@@ -79,6 +79,11 @@ public class AlerterProperties {
|
||||
*/
|
||||
private EntranceProperties entrance;
|
||||
|
||||
/**
|
||||
* Inhibit configuration properties
|
||||
*/
|
||||
private InhibitProperties inhibit;
|
||||
|
||||
/**
|
||||
* Data entry configuration properties
|
||||
*/
|
||||
@@ -105,4 +110,17 @@ public class AlerterProperties {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inhibit configuration properties
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public static class InhibitProperties {
|
||||
|
||||
/**
|
||||
* inhibit rule cache ttl, default 4h
|
||||
*/
|
||||
private long ttl = 4 * 60 * 60 * 1000L;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class AlerterWorkerPool {
|
||||
private void initWorkExecutor() {
|
||||
ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setUncaughtExceptionHandler((thread, throwable) -> {
|
||||
log.error("workerExecutor has uncaughtException.");
|
||||
log.error("Alerter workerExecutor has uncaughtException.");
|
||||
log.error(throwable.getMessage(), throwable);
|
||||
})
|
||||
.setDaemon(true)
|
||||
@@ -62,7 +62,7 @@ public class AlerterWorkerPool {
|
||||
private void initNotifyExecutor() {
|
||||
ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setUncaughtExceptionHandler((thread, throwable) -> {
|
||||
log.error("notifyExecutor has uncaughtException.");
|
||||
log.error("Alerter notifyExecutor has uncaughtException.");
|
||||
log.error(throwable.getMessage(), throwable);
|
||||
})
|
||||
.setDaemon(true)
|
||||
|
||||
-492
@@ -1,492 +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 com.google.common.collect.Maps;
|
||||
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.constants.MetricDataConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.arrow.RowWrapper;
|
||||
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";
|
||||
|
||||
private static final int CALCULATE_THREADS = 3;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
dataQueue.sendMetricsDataToStorage(metricsData);
|
||||
} catch (InterruptedException ignored) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception e) {
|
||||
log.error("calculate alarm error: {}.", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (int i = 0; i < CALCULATE_THREADS; i++) {
|
||||
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;
|
||||
}
|
||||
Map<String, Object> fieldValueMap = Maps.newHashMapWithExpectedSize(8);
|
||||
|
||||
try {
|
||||
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.getValues().size() == 0L) {
|
||||
fieldValueMap.put(SYSTEM_VALUE_ROW_COUNT, 0);
|
||||
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) {}
|
||||
}
|
||||
|
||||
RowWrapper rowWrapper = metricsData.readRow();
|
||||
while (rowWrapper.hasNextRow()) {
|
||||
rowWrapper = rowWrapper.nextRow();
|
||||
StringBuilder tagBuilder = new StringBuilder();
|
||||
fieldValueMap.clear();
|
||||
fieldValueMap.put(SYSTEM_VALUE_ROW_COUNT, metricsData.getValues().size());
|
||||
rowWrapper.cellStream().forEach(cell -> {
|
||||
String valueStr = cell.getValue();
|
||||
if (CommonConstants.NULL_VALUE.equals(valueStr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String fieldName = cell.getField().getName();
|
||||
final int fieldType = cell.getMetadataAsInteger(MetricDataConstants.TYPE);
|
||||
|
||||
if (fieldType == CommonConstants.TYPE_NUMBER) {
|
||||
final Double doubleValue;
|
||||
if ((doubleValue = CommonUtil.parseStrDouble(valueStr)) != null) {
|
||||
fieldValueMap.put(fieldName, doubleValue);
|
||||
}
|
||||
} else if (fieldType == CommonConstants.TYPE_TIME) {
|
||||
final Integer integerValue;
|
||||
if ((integerValue = CommonUtil.parseStrInteger(valueStr)) != null) {
|
||||
fieldValueMap.put(fieldName, integerValue);
|
||||
}
|
||||
} else {
|
||||
if (StringUtils.isNotEmpty(valueStr)) {
|
||||
fieldValueMap.put(fieldName, valueStr);
|
||||
}
|
||||
}
|
||||
|
||||
if (cell.getMetadataAsBoolean(MetricDataConstants.LABEL)) {
|
||||
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) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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 java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
|
||||
import org.apache.hertzbeat.alert.service.DataSourceService;
|
||||
import org.apache.hertzbeat.alert.util.AlertTemplateUtil;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Periodic Alert Calculator
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PeriodicAlertCalculator {
|
||||
|
||||
private static final String VALUE = "__value__";
|
||||
private static final String TIMESTAMP = "__timestamp__";
|
||||
|
||||
private final DataSourceService dataSourceService;
|
||||
private final AlarmCommonReduce alarmCommonReduce;
|
||||
/**
|
||||
* The alarm in the process is triggered
|
||||
* key - labels fingerprint
|
||||
*/
|
||||
private final Map<String, SingleAlert> pendingAlertMap;
|
||||
/**
|
||||
* The not recover alert
|
||||
* key - labels fingerprint
|
||||
*/
|
||||
private final Map<String, SingleAlert> firingAlertMap;
|
||||
|
||||
public PeriodicAlertCalculator(DataSourceService dataSourceService, AlarmCommonReduce alarmCommonReduce) {
|
||||
this.dataSourceService = dataSourceService;
|
||||
this.alarmCommonReduce = alarmCommonReduce;
|
||||
this.pendingAlertMap = new ConcurrentHashMap<>(8);
|
||||
this.firingAlertMap = new ConcurrentHashMap<>(8);
|
||||
}
|
||||
|
||||
public void calculate(AlertDefine rule) {
|
||||
if (!rule.isEnable() || StringUtils.isEmpty(rule.getExpr())) {
|
||||
log.error("Periodic rule {} is disabled or expression is empty", rule.getName());
|
||||
return;
|
||||
}
|
||||
long currentTimeMilli = System.currentTimeMillis();
|
||||
try {
|
||||
// for prometheus is instant promql query, for db is sql query
|
||||
// result: [{'value': 100, 'timestamp': 1343554, 'instance': 'node1'},{'value': 200, 'timestamp': 1343555, 'instance': 'node2'}]
|
||||
// the return result should be matched with threshold
|
||||
try {
|
||||
List<Map<String, Object>> results = dataSourceService.calculate(
|
||||
rule.getDatasource(),
|
||||
rule.getExpr()
|
||||
);
|
||||
// if no match the expr threshold, the results item map {'value': null} should be null and others field keep
|
||||
// if results has multi list, should trigger multi alert
|
||||
if (CollectionUtils.isEmpty(results)) {
|
||||
return;
|
||||
}
|
||||
for (Map<String, Object> result : results) {
|
||||
Map<String, String> fingerPrints = new HashMap<>(8);
|
||||
// here use the alert name as finger, not care the alert name may be changed
|
||||
fingerPrints.put(CommonConstants.LABEL_ALERT_NAME, rule.getName());
|
||||
fingerPrints.putAll(rule.getLabels());
|
||||
for (Map.Entry<String, Object> entry : result.entrySet()) {
|
||||
if (entry.getValue() != null && !VALUE.equals(entry.getKey())
|
||||
&& !TIMESTAMP.equals(entry.getKey())) {
|
||||
fingerPrints.put(entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
if (result.get(VALUE) == null) {
|
||||
// recovery the alert
|
||||
handleRecoveredAlert(fingerPrints);
|
||||
continue;
|
||||
}
|
||||
Map<String, Object> fieldValueMap = new HashMap<>(8);
|
||||
fieldValueMap.putAll(rule.getLabels());
|
||||
fieldValueMap.put(CommonConstants.LABEL_ALERT_NAME, rule.getName());
|
||||
for (Map.Entry<String, Object> entry : result.entrySet()) {
|
||||
if (entry.getValue() != null) {
|
||||
fieldValueMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
afterThresholdRuleMatch(currentTimeMilli, fingerPrints, fieldValueMap, rule);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// ignore the query exception eg: no result, timeout, etc
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Calculate periodic rule {} failed: {}", rule.getName(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void afterThresholdRuleMatch(long currentTimeMilli, Map<String, String> fingerPrints,
|
||||
Map<String, Object> fieldValueMap, AlertDefine define) {
|
||||
String fingerprint = calculateFingerprint(fingerPrints);
|
||||
SingleAlert existingAlert = pendingAlertMap.get(fingerprint);
|
||||
Map<String, String> labels = new HashMap<>(8);
|
||||
fieldValueMap.putAll(define.getLabels());
|
||||
labels.putAll(fingerPrints);
|
||||
int requiredTimes = define.getTimes() == null ? 1 : define.getTimes();
|
||||
if (existingAlert == null) {
|
||||
// First time triggering alert, create new alert and set to pending status
|
||||
SingleAlert newAlert = SingleAlert.builder()
|
||||
.labels(labels)
|
||||
// todo render var content in annotations
|
||||
.annotations(define.getAnnotations())
|
||||
.content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap))
|
||||
.status(CommonConstants.ALERT_STATUS_PENDING)
|
||||
.triggerTimes(1)
|
||||
.startAt(currentTimeMilli)
|
||||
.activeAt(currentTimeMilli)
|
||||
.build();
|
||||
|
||||
// If required trigger times is 1, set to firing status directly
|
||||
if (requiredTimes <= 1) {
|
||||
newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
|
||||
firingAlertMap.put(fingerprint, newAlert);
|
||||
alarmCommonReduce.reduceAndSendAlarm(newAlert.clone());
|
||||
} else {
|
||||
// Otherwise put into pending queue first
|
||||
pendingAlertMap.put(fingerprint, newAlert);
|
||||
}
|
||||
} else {
|
||||
// Update existing alert
|
||||
existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1);
|
||||
existingAlert.setActiveAt(currentTimeMilli);
|
||||
|
||||
// Check if required trigger times reached
|
||||
if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) {
|
||||
// Reached trigger times threshold, change to firing status
|
||||
pendingAlertMap.remove(fingerprint);
|
||||
existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
|
||||
firingAlertMap.put(fingerprint, existingAlert);
|
||||
alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRecoveredAlert(Map<String, String> fingerprints) {
|
||||
String fingerprint = calculateFingerprint(fingerprints);
|
||||
SingleAlert firingAlert = firingAlertMap.remove(fingerprint);
|
||||
if (firingAlert != null) {
|
||||
// todo consider multi times to tig for resolved alert
|
||||
firingAlert.setTriggerTimes(1);
|
||||
firingAlert.setEndAt(System.currentTimeMillis());
|
||||
firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED);
|
||||
alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone());
|
||||
}
|
||||
pendingAlertMap.remove(fingerprint);
|
||||
}
|
||||
|
||||
private String calculateFingerprint(Map<String, String> fingerPrints) {
|
||||
List<String> keyList = fingerPrints.keySet().stream().filter(Objects::nonNull).sorted().toList();
|
||||
List<String> valueList = fingerPrints.values().stream().filter(Objects::nonNull).sorted().toList();
|
||||
return Arrays.hashCode(keyList.toArray(new String[0])) + "-"
|
||||
+ Arrays.hashCode(valueList.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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_THRESHOLD_TYPE_PERIODIC;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDefineDao;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
|
||||
/**
|
||||
* Periodic Alert Rule Scheduler
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PeriodicAlertRuleScheduler implements CommandLineRunner {
|
||||
|
||||
private final PeriodicAlertCalculator calculator;
|
||||
private final AlertDefineDao alertDefineDao;
|
||||
private final ScheduledExecutorService scheduledExecutor;
|
||||
private final Map<Long, ScheduledFuture<?>> scheduledFutures;
|
||||
|
||||
public PeriodicAlertRuleScheduler(PeriodicAlertCalculator calculator, AlertDefineDao alertDefineDao) {
|
||||
this.calculator = calculator;
|
||||
this.alertDefineDao = alertDefineDao;
|
||||
ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setUncaughtExceptionHandler((thread, throwable) -> {
|
||||
log.error("Scheduled periodic alert threshold has uncaughtException.");
|
||||
log.error(throwable.getMessage(), throwable);
|
||||
})
|
||||
.setDaemon(true)
|
||||
.setNameFormat("periodic-alert-threshold-worker-%d")
|
||||
.build();
|
||||
this.scheduledExecutor = Executors.newScheduledThreadPool(10, threadFactory);
|
||||
this.scheduledFutures = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public void cancelSchedule(Long ruleId) {
|
||||
if (ruleId == null) {
|
||||
return;
|
||||
}
|
||||
ScheduledFuture<?> future = scheduledFutures.get(ruleId);
|
||||
if (future != null) {
|
||||
future.cancel(true);
|
||||
scheduledFutures.remove(ruleId);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateSchedule(AlertDefine rule) {
|
||||
if (rule == null || rule.getId() == null) {
|
||||
log.error("Alert rule is null or rule id is null.");
|
||||
return;
|
||||
}
|
||||
cancelSchedule(rule.getId());
|
||||
if (rule.getType().equals(ALERT_THRESHOLD_TYPE_PERIODIC)) {
|
||||
ScheduledFuture<?> future = scheduledExecutor.scheduleAtFixedRate(() -> {
|
||||
calculator.calculate(rule);
|
||||
}, 0, rule.getPeriod(), java.util.concurrent.TimeUnit.SECONDS);
|
||||
scheduledFutures.put(rule.getId(), future);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
log.info("Starting periodic alert rule scheduler...");
|
||||
List<AlertDefine> periodicRules = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(ALERT_THRESHOLD_TYPE_PERIODIC);
|
||||
for (AlertDefine rule : periodicRules) {
|
||||
updateSchedule(rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* 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 java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
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.SingleAlertDao;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineService;
|
||||
import org.apache.hertzbeat.alert.util.AlertTemplateUtil;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.apache.hertzbeat.common.entity.message.CollectRep;
|
||||
import org.apache.hertzbeat.common.queue.CommonDataQueue;
|
||||
import org.apache.hertzbeat.common.util.CommonUtil;
|
||||
import org.apache.hertzbeat.common.util.JexlExpressionRunner;
|
||||
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 RealTimeAlertCalculator {
|
||||
|
||||
private static final int CALCULATE_THREADS = 3;
|
||||
|
||||
private static final String KEY_INSTANCE = "__instance__";
|
||||
private static final String KEY_APP = "__app__";
|
||||
private static final String KEY_METRICS = "__metrics__";
|
||||
private static final String KEY_PRIORITY = "__priority__";
|
||||
private static final String KEY_CODE = "__code__";
|
||||
private static final String KEY_AVAILABLE = "__available__";
|
||||
private static final String UP = "up";
|
||||
private static final String DOWN = "down";
|
||||
private static final String KEY_ROW = "__row__";
|
||||
|
||||
private static final Pattern APP_PATTERN = Pattern.compile("equals\\(__app__,\"([^\"]+)\"\\)");
|
||||
private static final Pattern AVAILABLE_PATTERN = Pattern.compile("equals\\(__available__,\"([^\"]+)\"\\)");
|
||||
private static final Pattern INSTANCE_PATTERN = Pattern.compile("equals\\(__instance__,\"(\\d+)\"\\)");
|
||||
private static final Pattern METRICS_PATTERN = Pattern.compile("equals\\(__metrics__,\"([^\"]+)\"\\)");
|
||||
|
||||
/**
|
||||
* The alarm in the process is triggered
|
||||
* key - labels fingerprint
|
||||
*/
|
||||
private final Map<String, SingleAlert> pendingAlertMap;
|
||||
/**
|
||||
* The not recover alert
|
||||
* key - labels fingerprint
|
||||
*/
|
||||
private final Map<String, SingleAlert> firingAlertMap;
|
||||
private final AlerterWorkerPool workerPool;
|
||||
private final CommonDataQueue dataQueue;
|
||||
private final AlertDefineService alertDefineService;
|
||||
private final AlarmCommonReduce alarmCommonReduce;
|
||||
|
||||
public RealTimeAlertCalculator(AlerterWorkerPool workerPool, CommonDataQueue dataQueue,
|
||||
AlertDefineService alertDefineService, SingleAlertDao singleAlertDao,
|
||||
AlarmCommonReduce alarmCommonReduce) {
|
||||
this.workerPool = workerPool;
|
||||
this.dataQueue = dataQueue;
|
||||
this.alarmCommonReduce = alarmCommonReduce;
|
||||
this.alertDefineService = alertDefineService;
|
||||
this.pendingAlertMap = new ConcurrentHashMap<>(8);
|
||||
this.firingAlertMap = new ConcurrentHashMap<>(8);
|
||||
// Initialize firing stateAlertMap
|
||||
List<SingleAlert> singleAlerts = singleAlertDao.querySingleAlertsByStatus(CommonConstants.ALERT_STATUS_FIRING);
|
||||
for (SingleAlert singleAlert : singleAlerts) {
|
||||
String fingerprint = calculateFingerprint(singleAlert.getLabels());
|
||||
singleAlert.setId(null);
|
||||
firingAlertMap.put(fingerprint, singleAlert);
|
||||
}
|
||||
startCalculate();
|
||||
}
|
||||
|
||||
private void startCalculate() {
|
||||
Runnable runnable = () -> {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
try {
|
||||
CollectRep.MetricsData metricsData = dataQueue.pollMetricsDataToAlerter();
|
||||
calculate(metricsData);
|
||||
dataQueue.sendMetricsDataToStorage(metricsData);
|
||||
} catch (InterruptedException ignored) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception e) {
|
||||
log.error("calculate alarm error: {}.", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (int i = 0; i < CALCULATE_THREADS; i++) {
|
||||
workerPool.executeJob(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
private void calculate(CollectRep.MetricsData metricsData) {
|
||||
long currentTimeMilli = System.currentTimeMillis();
|
||||
String instance = String.valueOf(metricsData.getId());
|
||||
String app = metricsData.getApp();
|
||||
String metrics = metricsData.getMetrics();
|
||||
int priority = metricsData.getPriority();
|
||||
int code = metricsData.getCode().getNumber();
|
||||
List<AlertDefine> thresholds = this.alertDefineService.getRealTimeAlertDefines();
|
||||
// Filter thresholds by app, metrics and instance
|
||||
thresholds = filterThresholdsByAppAndMetrics(thresholds, app, metrics, instance, priority);
|
||||
if (thresholds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> commonContext = new HashMap<>(8);
|
||||
commonContext.put(KEY_INSTANCE, instance);
|
||||
commonContext.put(KEY_APP, app);
|
||||
commonContext.put(KEY_PRIORITY, priority);
|
||||
commonContext.put(KEY_CODE, code);
|
||||
commonContext.put(KEY_METRICS, metrics);
|
||||
if (priority == 0) {
|
||||
commonContext.put(KEY_AVAILABLE, metricsData.getCode() == CollectRep.Code.SUCCESS ? UP : DOWN);
|
||||
}
|
||||
List<CollectRep.Field> fields = metricsData.getFields();
|
||||
Map<String, Object> fieldValueMap = new HashMap<>(8);
|
||||
int valueRowCount = metricsData.getValuesCount();
|
||||
for (AlertDefine define : thresholds) {
|
||||
if (define.getLabels() == null) {
|
||||
define.setLabels(new HashMap<>(8));
|
||||
}
|
||||
if (define.getAnnotations() == null) {
|
||||
define.setAnnotations(new HashMap<>(8));
|
||||
}
|
||||
fieldValueMap.clear();
|
||||
fieldValueMap.putAll(commonContext);
|
||||
final String expr = define.getExpr();
|
||||
if (StringUtils.isBlank(expr)) {
|
||||
continue;
|
||||
}
|
||||
{
|
||||
// trigger the expr before the metrics data, due the available up down or others
|
||||
try {
|
||||
boolean match = execAlertExpression(fieldValueMap, expr, true);
|
||||
try {
|
||||
Map<String, String> fingerPrints = new HashMap<>(8);
|
||||
fingerPrints.put(CommonConstants.LABEL_INSTANCE, instance);
|
||||
// here use the alert name as finger, not care the alert name may be changed
|
||||
fingerPrints.put(CommonConstants.LABEL_ALERT_NAME, define.getName());
|
||||
fingerPrints.putAll(define.getLabels());
|
||||
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, fingerPrints, fieldValueMap, define);
|
||||
} else {
|
||||
handleRecoveredAlert(fingerPrints);
|
||||
}
|
||||
// if this threshold pre compile success, ignore blew
|
||||
continue;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
Map<String, String> fingerPrints = new HashMap<>(8);
|
||||
for (CollectRep.ValueRow valueRow : metricsData.getValues()) {
|
||||
if (CollectionUtils.isEmpty(valueRow.getColumnsList())) {
|
||||
continue;
|
||||
}
|
||||
fieldValueMap.clear();
|
||||
fieldValueMap.put(KEY_ROW, valueRowCount);
|
||||
fieldValueMap.putAll(commonContext);
|
||||
fingerPrints.clear();
|
||||
fingerPrints.put(CommonConstants.LABEL_INSTANCE, instance);
|
||||
fingerPrints.put(CommonConstants.LABEL_ALERT_NAME, define.getName());
|
||||
fingerPrints.putAll(define.getLabels());
|
||||
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()) {
|
||||
fingerPrints.put(field.getName(), valueStr);
|
||||
}
|
||||
}
|
||||
try {
|
||||
boolean match = execAlertExpression(fieldValueMap, expr, false);
|
||||
try {
|
||||
if (match) {
|
||||
afterThresholdRuleMatch(currentTimeMilli, fingerPrints, fieldValueMap, define);
|
||||
} else {
|
||||
handleRecoveredAlert(fingerPrints);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter alert definitions by app, metrics and instance
|
||||
*
|
||||
* @param thresholds Alert definitions to filter
|
||||
* @param app Current app name
|
||||
* @param metrics Current metrics name
|
||||
* @param instance Current instance id
|
||||
* @param priority Current priority
|
||||
* @return Filtered alert definitions
|
||||
*/
|
||||
private List<AlertDefine> filterThresholdsByAppAndMetrics(List<AlertDefine> thresholds, String app, String metrics, String instance, int priority) {
|
||||
return thresholds.stream()
|
||||
.filter(define -> {
|
||||
if (StringUtils.isBlank(define.getExpr())) {
|
||||
return false;
|
||||
}
|
||||
String expr = define.getExpr();
|
||||
|
||||
// Extract and check app - required
|
||||
Matcher appMatcher = APP_PATTERN.matcher(expr);
|
||||
if (!appMatcher.find() || !app.equals(appMatcher.group(1))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract and check available - required
|
||||
if (priority != 0) {
|
||||
Matcher availableMatcher = AVAILABLE_PATTERN.matcher(expr);
|
||||
if (availableMatcher.find()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract and check metrics - optional
|
||||
Matcher metricsMatcher = METRICS_PATTERN.matcher(expr);
|
||||
if (metricsMatcher.find() && !metrics.equals(metricsMatcher.group(1))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract and check instance - optional with multiple values
|
||||
Matcher instanceMatcher = INSTANCE_PATTERN.matcher(expr);
|
||||
// If no instance specified in expr, accept all instances
|
||||
if (!instanceMatcher.find()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reset matcher to check all instances
|
||||
instanceMatcher.reset();
|
||||
// If instances specified, current instance must match one of them
|
||||
while (instanceMatcher.find()) {
|
||||
if (Objects.equals(instance, instanceMatcher.group(1))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void handleRecoveredAlert(Map<String, String> fingerprints) {
|
||||
String fingerprint = calculateFingerprint(fingerprints);
|
||||
SingleAlert firingAlert = firingAlertMap.remove(fingerprint);
|
||||
if (firingAlert != null) {
|
||||
// todo consider multi times to tig for resolved alert
|
||||
firingAlert.setTriggerTimes(1);
|
||||
firingAlert.setEndAt(System.currentTimeMillis());
|
||||
firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED);
|
||||
alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone());
|
||||
}
|
||||
pendingAlertMap.remove(fingerprint);
|
||||
}
|
||||
|
||||
private void afterThresholdRuleMatch(long currentTimeMilli, Map<String, String> fingerPrints,
|
||||
Map<String, Object> fieldValueMap, AlertDefine define) {
|
||||
String fingerprint = calculateFingerprint(fingerPrints);
|
||||
SingleAlert existingAlert = pendingAlertMap.get(fingerprint);
|
||||
Map<String, String> labels = new HashMap<>(8);
|
||||
fieldValueMap.putAll(define.getLabels());
|
||||
labels.putAll(fingerPrints);
|
||||
int requiredTimes = define.getTimes() == null ? 1 : define.getTimes();
|
||||
if (existingAlert == null) {
|
||||
// First time triggering alert, create new alert and set to pending status
|
||||
SingleAlert newAlert = SingleAlert.builder()
|
||||
.labels(labels)
|
||||
// todo render var content in annotations
|
||||
.annotations(define.getAnnotations())
|
||||
.content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap))
|
||||
.status(CommonConstants.ALERT_STATUS_PENDING)
|
||||
.triggerTimes(1)
|
||||
.startAt(currentTimeMilli)
|
||||
.activeAt(currentTimeMilli)
|
||||
.build();
|
||||
|
||||
// If required trigger times is 1, set to firing status directly
|
||||
if (requiredTimes <= 1) {
|
||||
newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
|
||||
firingAlertMap.put(fingerprint, newAlert);
|
||||
alarmCommonReduce.reduceAndSendAlarm(newAlert.clone());
|
||||
} else {
|
||||
// Otherwise put into pending queue first
|
||||
pendingAlertMap.put(fingerprint, newAlert);
|
||||
}
|
||||
} else {
|
||||
// Update existing alert
|
||||
existingAlert.setTriggerTimes(existingAlert.getTriggerTimes() + 1);
|
||||
existingAlert.setActiveAt(currentTimeMilli);
|
||||
|
||||
// Check if required trigger times reached
|
||||
if (existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && existingAlert.getTriggerTimes() >= requiredTimes) {
|
||||
// Reached trigger times threshold, change to firing status
|
||||
pendingAlertMap.remove(fingerprint);
|
||||
existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
|
||||
firingAlertMap.put(fingerprint, existingAlert);
|
||||
alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean execAlertExpression(Map<String, Object> fieldValueMap, String expr, boolean ignoreJexlException) {
|
||||
Boolean match;
|
||||
JexlExpression expression;
|
||||
try {
|
||||
expression = JexlExpressionRunner.compile(expr);
|
||||
} catch (JexlException jexlException) {
|
||||
log.warn("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) {
|
||||
if (ignoreJexlException) {
|
||||
log.debug("Alarm Rule: {} Run Error: {}.", expr, jexlException.getMessage());
|
||||
} else {
|
||||
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 String calculateFingerprint(Map<String, String> fingerPrints) {
|
||||
List<String> keyList = fingerPrints.keySet().stream().filter(Objects::nonNull).sorted().toList();
|
||||
List<String> valueList = fingerPrints.values().stream().filter(Objects::nonNull).sorted().toList();
|
||||
return Arrays.hashCode(keyList.toArray(new String[0])) + "-"
|
||||
+ Arrays.hashCode(valueList.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* SSE manager for alert
|
||||
*/
|
||||
@Component
|
||||
public class AlertSseManager {
|
||||
private final Map<Long, SseEmitter> emitters = new ConcurrentHashMap<>();
|
||||
|
||||
public SseEmitter createEmitter(Long clientId) {
|
||||
SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
|
||||
emitter.onCompletion(() -> removeEmitter(clientId));
|
||||
emitter.onTimeout(() -> removeEmitter(clientId));
|
||||
emitters.put(clientId, emitter);
|
||||
return emitter;
|
||||
}
|
||||
|
||||
@Async
|
||||
public void broadcast(String data) {
|
||||
emitters.forEach((clientId, emitter) -> {
|
||||
try {
|
||||
emitter.send(SseEmitter.event()
|
||||
.id(String.valueOf(System.currentTimeMillis()))
|
||||
.name("ALERT_EVENT")
|
||||
.data(data));
|
||||
} catch (IOException e) {
|
||||
emitter.complete();
|
||||
removeEmitter(clientId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeEmitter(Long clientId) {
|
||||
emitters.remove(clientId);
|
||||
}
|
||||
}
|
||||
-23
@@ -23,12 +23,9 @@ 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.Objects;
|
||||
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;
|
||||
@@ -93,24 +90,4 @@ public class AlertDefineController {
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-2
@@ -55,12 +55,11 @@ public class AlertDefinesController {
|
||||
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) {
|
||||
Page<AlertDefine> alertDefinePage = alertDefineService.getAlertDefines(ids, search, priority, sort, order, pageIndex, pageSize);
|
||||
Page<AlertDefine> alertDefinePage = alertDefineService.getAlertDefines(ids, search, sort, order, pageIndex, pageSize);
|
||||
return ResponseEntity.ok(Message.success(alertDefinePage));
|
||||
}
|
||||
|
||||
|
||||
+20
-20
@@ -24,8 +24,8 @@ import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.Objects;
|
||||
import org.apache.hertzbeat.alert.service.AlertConvergeService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertConverge;
|
||||
import org.apache.hertzbeat.alert.service.AlertGroupConvergeService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -42,38 +42,38 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
*/
|
||||
@Tag(name = "Alert Converge API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/converge", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertConvergeController {
|
||||
@RequestMapping(path = "/api/alert/group", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertGroupConvergeController {
|
||||
|
||||
@Autowired
|
||||
private AlertConvergeService alertConvergeService;
|
||||
private AlertGroupConvergeService alertGroupConvergeService;
|
||||
|
||||
@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);
|
||||
@Operation(summary = "New Alarm Group Converge", description = "Added an alarm Group Converge")
|
||||
public ResponseEntity<Message<Void>> addNewAlertGroupConverge(@Valid @RequestBody AlertGroupConverge alertGroupConverge) {
|
||||
alertGroupConvergeService.validate(alertGroupConverge, false);
|
||||
alertGroupConvergeService.addAlertGroupConverge(alertGroupConverge);
|
||||
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);
|
||||
@Operation(summary = "Modifying an Alarm Group Converge", description = "Modify an existing alarm Group Converge")
|
||||
public ResponseEntity<Message<Void>> modifyAlertGroupConverge(@Valid @RequestBody AlertGroupConverge alertGroupConverge) {
|
||||
alertGroupConvergeService.validate(alertGroupConverge, true);
|
||||
alertGroupConvergeService.modifyAlertGroupConverge(alertGroupConverge);
|
||||
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(
|
||||
@Operation(summary = "Querying Alarm Group Converge",
|
||||
description = "You can obtain alarm Group Converge information based on the alarm Converge ID")
|
||||
public ResponseEntity<Message<AlertGroupConverge>> getAlertGroupConverge(
|
||||
@Parameter(description = "Alarm Converge ID", example = "6565463543") @PathVariable("id") long id) {
|
||||
AlertConverge alertConverge = alertConvergeService.getAlertConverge(id);
|
||||
AlertGroupConverge alertGroupConverge = alertGroupConvergeService.getAlertGroupConverge(id);
|
||||
|
||||
return Objects.isNull(alertConverge)
|
||||
? ResponseEntity.ok(Message.fail(MONITOR_NOT_EXIST_CODE, "AlertConverge not exist."))
|
||||
: ResponseEntity.ok(Message.success(alertConverge));
|
||||
return Objects.isNull(alertGroupConverge)
|
||||
? ResponseEntity.ok(Message.fail(MONITOR_NOT_EXIST_CODE, "Alert Group Converge not exist."))
|
||||
: ResponseEntity.ok(Message.success(alertGroupConverge));
|
||||
}
|
||||
|
||||
}
|
||||
+13
-13
@@ -23,8 +23,8 @@ import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
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.alert.service.AlertGroupConvergeService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -40,34 +40,34 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
*/
|
||||
@Tag(name = "Alert Converge Batch API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/converges", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertConvergesController {
|
||||
@RequestMapping(path = "/api/alert/groups", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertGroupConvergesController {
|
||||
|
||||
@Autowired
|
||||
private AlertConvergeService alertConvergeService;
|
||||
private AlertGroupConvergeService alertGroupConvergeService;
|
||||
|
||||
@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(
|
||||
@Operation(summary = "Query the alarm group converge list",
|
||||
description = "You can obtain the list of alarm group group converge by querying filter items")
|
||||
public ResponseEntity<Message<Page<AlertGroupConverge>>> getAlertGroupConverges(
|
||||
@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) {
|
||||
Page<AlertConverge> alertConvergePage = alertConvergeService.getAlertConverges(ids, search, sort, order, pageIndex, pageSize);
|
||||
return ResponseEntity.ok(Message.success(alertConvergePage));
|
||||
Page<AlertGroupConverge> alertGroupConvergePage = alertGroupConvergeService.getAlertGroupConverges(ids, search, sort, order, pageIndex, pageSize);
|
||||
return ResponseEntity.ok(Message.success(alertGroupConvergePage));
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@Operation(summary = "Delete alarm converge in batches",
|
||||
description = "Delete alarm converge in batches based on the alarm converge ID list")
|
||||
@Operation(summary = "Delete alarm group converge in batches",
|
||||
description = "Delete alarm group converge in batches based on the alarm group 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));
|
||||
alertGroupConvergeService.deleteAlertGroupConverges(new HashSet<>(ids));
|
||||
}
|
||||
return ResponseEntity.ok(Message.success());
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.Objects;
|
||||
import org.apache.hertzbeat.alert.service.AlertInhibitService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertInhibit;
|
||||
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 Inhibit management API
|
||||
*/
|
||||
@Tag(name = "Alert Inhibit API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/inhibit", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertInhibitController {
|
||||
|
||||
@Autowired
|
||||
private AlertInhibitService alertInhibitService;
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "New Alarm Inhibit", description = "Added an alarm Inhibit")
|
||||
public ResponseEntity<Message<Void>> addNewAlertInhibit(@Valid @RequestBody AlertInhibit alertInhibit) {
|
||||
alertInhibitService.validate(alertInhibit, false);
|
||||
alertInhibitService.addAlertInhibit(alertInhibit);
|
||||
return ResponseEntity.ok(Message.success("Add success"));
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Operation(summary = "Modifying an Alarm Inhibit", description = "Modify an existing alarm Inhibit")
|
||||
public ResponseEntity<Message<Void>> modifyAlertInhibit(@Valid @RequestBody AlertInhibit alertInhibit) {
|
||||
alertInhibitService.validate(alertInhibit, true);
|
||||
alertInhibitService.modifyAlertInhibit(alertInhibit);
|
||||
return ResponseEntity.ok(Message.success("Modify success"));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{id}")
|
||||
@Operation(summary = "Querying Alarm Inhibit",
|
||||
description = "You can obtain alarm Inhibit information based on the alarm Inhibit ID")
|
||||
public ResponseEntity<Message<AlertInhibit>> getAlertInhibit(
|
||||
@Parameter(description = "Alarm Inhibit ID", example = "6565463543") @PathVariable("id") long id) {
|
||||
AlertInhibit alertInhibit = alertInhibitService.getAlertInhibit(id);
|
||||
|
||||
return Objects.isNull(alertInhibit)
|
||||
? ResponseEntity.ok(Message.fail(MONITOR_NOT_EXIST_CODE, "AlertInhibit not exist."))
|
||||
: ResponseEntity.ok(Message.success(alertInhibit));
|
||||
}
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 java.util.HashSet;
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.alert.service.AlertInhibitService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertInhibit;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
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.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Inhibit the batch API for alarms
|
||||
*/
|
||||
@Tag(name = "Alert Inhibit Batch API")
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/inhibits", produces = {APPLICATION_JSON_VALUE})
|
||||
public class AlertInhibitsController {
|
||||
|
||||
@Autowired
|
||||
private AlertInhibitService alertInhibitService;
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "Query the alarm inhibit list",
|
||||
description = "You can obtain the list of alarm inhibit by querying filter items")
|
||||
public ResponseEntity<Message<Page<AlertInhibit>>> getAlertInhibits(
|
||||
@Parameter(description = "Alarm Inhibit 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) {
|
||||
Page<AlertInhibit> alertInhibitPage = alertInhibitService.getAlertInhibits(ids, search, sort, order, pageIndex, pageSize);
|
||||
return ResponseEntity.ok(Message.success(alertInhibitPage));
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@Operation(summary = "Delete alarm inhibit in batches",
|
||||
description = "Delete alarm inhibit in batches based on the alarm inhibit ID list")
|
||||
public ResponseEntity<Message<Void>> deleteAlertDefines(
|
||||
@Parameter(description = "Alarm Inhibit IDs", example = "6565463543") @RequestParam(required = false) List<Long> ids
|
||||
) {
|
||||
if (ids != null && !ids.isEmpty()) {
|
||||
alertInhibitService.deleteAlertInhibits(new HashSet<>(ids));
|
||||
}
|
||||
Message<Void> message = Message.success();
|
||||
return ResponseEntity.ok(message);
|
||||
}
|
||||
|
||||
}
|
||||
+76
-24
@@ -17,19 +17,19 @@
|
||||
|
||||
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.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dto.GeneralCloudAlertReport;
|
||||
import org.apache.hertzbeat.alert.service.AlertService;
|
||||
import org.apache.hertzbeat.alert.service.ExternAlertService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.StringUtils;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -37,27 +37,79 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
*/
|
||||
@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) {
|
||||
alertService.addNewAlertReportFromCloud(cloudServiceName, alertReport);
|
||||
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"));
|
||||
private final List<ExternAlertService> externAlertServiceList;
|
||||
|
||||
public AlertReportController(List<ExternAlertService> externAlertServiceList) {
|
||||
this.externAlertServiceList = externAlertServiceList;
|
||||
}
|
||||
|
||||
@PostMapping("/api/alerts/report/{source}")
|
||||
@Operation(summary = "Api for receive external alarm information")
|
||||
public ResponseEntity<Message<Void>> receiveExternAlert(@PathVariable(value = "source") String source,
|
||||
@RequestBody String content) {
|
||||
log.info("Receive extern alert from source: {}, content: {}", source, content);
|
||||
if (!StringUtils.hasText(source)) {
|
||||
source = "default";
|
||||
}
|
||||
for (ExternAlertService externAlertService : externAlertServiceList) {
|
||||
if (externAlertService.supportSource().equals(source)) {
|
||||
try {
|
||||
externAlertService.addExternAlert(content);
|
||||
return ResponseEntity.ok(Message.success("Add extern alert success"));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body(Message.fail(CommonConstants.FAIL_CODE,
|
||||
"Add extern alert failed: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
log.warn("Not support extern alert from source: {}", source);
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body(Message.fail(CommonConstants.FAIL_CODE, "Not support the " + source + " source alert"));
|
||||
}
|
||||
|
||||
@PostMapping("/api/alerts/report")
|
||||
@Operation(summary = "Api for receive default external alarm information")
|
||||
public ResponseEntity<Message<Void>> receiveDefaultExternAlert(@RequestBody String content) {
|
||||
log.info("Receive default extern alert content: {}", content);
|
||||
ExternAlertService externAlertService = externAlertServiceList.stream()
|
||||
.filter(item -> "default".equals(item.supportSource())).findFirst().orElse(null);
|
||||
if (externAlertService != null) {
|
||||
try {
|
||||
externAlertService.addExternAlert(content);
|
||||
return ResponseEntity.ok(Message.success("Add extern alert success"));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body(Message.fail(CommonConstants.FAIL_CODE,
|
||||
"Add extern alert failed: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
log.error("Not support default extern alert");
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body(Message.success("Not support the default source alert"));
|
||||
}
|
||||
|
||||
@PostMapping("/api/v2/alerts")
|
||||
@Operation(summary = "Api for receive external alarm information")
|
||||
public ResponseEntity<Message<Void>> receivePrometheusServerAlert(@RequestBody String content) {
|
||||
log.info("Receive prometheus server alert, content: {}", content);
|
||||
ExternAlertService externAlertService = externAlertServiceList.stream()
|
||||
.filter(item -> "prometheus".equals(item.supportSource())).findFirst().orElse(null);
|
||||
if (externAlertService != null) {
|
||||
try {
|
||||
externAlertService.addExternAlert(content);
|
||||
return ResponseEntity.ok(Message.success("Add extern alert success"));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body(Message.fail(CommonConstants.FAIL_CODE,
|
||||
"Add extern alert failed: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
log.error("Not support prometheus extern alert");
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body(Message.success("Not support the prometheus source alert"));
|
||||
}
|
||||
}
|
||||
|
||||
+48
@@ -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.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.controller;
|
||||
|
||||
import static org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE;
|
||||
import org.apache.hertzbeat.alert.config.AlertSseManager;
|
||||
import org.apache.hertzbeat.common.util.SnowFlakeIdGenerator;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
/**
|
||||
* SSE controller for alert
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/alert/sse", produces = {TEXT_EVENT_STREAM_VALUE})
|
||||
public class AlertSseController {
|
||||
|
||||
private final AlertSseManager emitterManager;
|
||||
|
||||
public AlertSseController(AlertSseManager emitterManager) {
|
||||
this.emitterManager = emitterManager;
|
||||
}
|
||||
|
||||
@GetMapping(path = "/subscribe")
|
||||
public SseEmitter subscribe() {
|
||||
Long clientId = SnowFlakeIdGenerator.generateId();
|
||||
return emitterManager.createEmitter(clientId);
|
||||
}
|
||||
}
|
||||
+28
-25
@@ -25,7 +25,8 @@ 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.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -50,47 +51,49 @@ public class AlertsController {
|
||||
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,
|
||||
@Operation(summary = "Query Alarms")
|
||||
public ResponseEntity<Message<Page<SingleAlert>>> getAlerts(
|
||||
@Parameter(description = "Alarm Status", example = "resolved") @RequestParam(required = false) String status,
|
||||
@Parameter(description = "Alarm content fuzzy query", example = "linux") @RequestParam(required = false) String search,
|
||||
@Parameter(description = "Sort field, default id", example = "name") @RequestParam(defaultValue = "gmtUpdate") 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) {
|
||||
Page<Alert> alertPage = alertService.getAlerts(ids, monitorId, priority, status, content, sort, order, pageIndex, pageSize);
|
||||
Page<SingleAlert> alertPage = alertService.getSingleAlerts(status, search, sort, order, pageIndex, pageSize);
|
||||
return ResponseEntity.ok(Message.success(alertPage));
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@Operation(summary = "Delete alarms in batches", description = "according to the alarm ID list to delete the alarm information in batches")
|
||||
@GetMapping("/group")
|
||||
@Operation(summary = "Query Group Alarms")
|
||||
public ResponseEntity<Message<Page<GroupAlert>>> getGroupAlerts(
|
||||
@Parameter(description = "Alarm Status", example = "resolved") @RequestParam(required = false) String status,
|
||||
@Parameter(description = "Alarm content fuzzy query", example = "linux") @RequestParam(required = false) String search,
|
||||
@Parameter(description = "Sort field, default id", example = "name") @RequestParam(defaultValue = "gmtUpdate") 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) {
|
||||
Page<GroupAlert> alertPage = alertService.getGroupAlerts(status, search, sort, order, pageIndex, pageSize);
|
||||
return ResponseEntity.ok(Message.success(alertPage));
|
||||
}
|
||||
|
||||
@DeleteMapping("/group")
|
||||
@Operation(summary = "Delete group 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));
|
||||
alertService.deleteGroupAlerts(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")
|
||||
@PutMapping(path = "/group/status/{status}")
|
||||
@Operation(summary = "Batch modify alarm status, set firing or resolved", description = "Batch modify alarm status, set firing or resolved")
|
||||
public ResponseEntity<Message<Void>> applyAlertDefinesStatus(
|
||||
@Parameter(description = "Alarm status value", example = "0") @PathVariable Byte status,
|
||||
@Parameter(description = "Alarm status value", example = "resolved") @PathVariable String 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);
|
||||
alertService.editGroupAlertStatus(status, ids);
|
||||
}
|
||||
Message<Void> message = Message.success();
|
||||
return ResponseEntity.ok(message);
|
||||
|
||||
+37
-15
@@ -15,22 +15,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.controller;
|
||||
package org.apache.hertzbeat.alert.controller;
|
||||
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.FAIL_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.Optional;
|
||||
import javax.validation.Valid;
|
||||
import org.apache.hertzbeat.common.entity.dto.Message;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.manager.service.NoticeConfigService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.alert.service.NoticeConfigService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -82,9 +83,18 @@ public class NoticeConfigController {
|
||||
@GetMapping(path = "/receivers")
|
||||
@Operation(summary = "Get a list of message notification recipients based on query filter items",
|
||||
description = "Get a list of message notification recipients based on query filter items")
|
||||
public ResponseEntity<Message<List<NoticeReceiver>>> getReceivers(
|
||||
@Parameter(description = "en: Recipient name,support fuzzy query", example = "tom") @RequestParam(required = false) final String name) {
|
||||
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeReceivers(name)));
|
||||
public ResponseEntity<Message<Page<NoticeReceiver>>> getReceivers(
|
||||
@Parameter(description = "en: Recipient name,support fuzzy query", example = "tom") @RequestParam(required = false) final String name,
|
||||
@Parameter(description = "en: List current page", example = "0") @RequestParam(defaultValue = "0") final int pageIndex,
|
||||
@Parameter(description = "en: Number of list pages", example = "8") @RequestParam(defaultValue = "8") final int pageSize) {
|
||||
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeReceivers(name, pageIndex, pageSize)));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/receivers/all")
|
||||
@Operation(summary = "Get a list of all message notification recipients",
|
||||
description = "Get a list of all message notification recipients")
|
||||
public ResponseEntity<Message<List<NoticeReceiver>>> getAllReceivers() {
|
||||
return ResponseEntity.ok(Message.success(noticeConfigService.getAllNoticeReceivers()));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/receiver/{id}")
|
||||
@@ -129,9 +139,11 @@ public class NoticeConfigController {
|
||||
@GetMapping(path = "/rules")
|
||||
@Operation(summary = "Get a list of message notification policies based on query filter items",
|
||||
description = "Get a list of message notification policies based on query filter items")
|
||||
public ResponseEntity<Message<List<NoticeRule>>> getRules(
|
||||
@Parameter(description = "en: Recipient name", example = "rule1") @RequestParam(required = false) final String name) {
|
||||
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeRules(name)));
|
||||
public ResponseEntity<Message<Page<NoticeRule>>> getRules(
|
||||
@Parameter(description = "en: Recipient name", example = "rule1") @RequestParam(required = false) final String name,
|
||||
@Parameter(description = "en: List current page", example = "0") @RequestParam(defaultValue = "0") final int pageIndex,
|
||||
@Parameter(description = "en: Number of list pages", example = "8") @RequestParam(defaultValue = "8") final int pageSize) {
|
||||
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeRules(name, pageIndex, pageSize)));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/rule/{id}")
|
||||
@@ -176,12 +188,22 @@ public class NoticeConfigController {
|
||||
@GetMapping(path = "/templates")
|
||||
@Operation(summary = "Get a list of message notification templates based on query filter items",
|
||||
description = "Get a list of message notification templates based on query filter items")
|
||||
public ResponseEntity<Message<List<NoticeTemplate>>> getTemplates(
|
||||
@Parameter(description = "Template name,support fuzzy query", example = "rule1") @RequestParam(required = false) final String name) {
|
||||
List<NoticeTemplate> templatePage = noticeConfigService.getNoticeTemplates(name);
|
||||
public ResponseEntity<Message<Page<NoticeTemplate>>> getTemplates(
|
||||
@Parameter(description = "Template name,support fuzzy query", example = "rule1") @RequestParam(required = false) final String name,
|
||||
@Parameter(description = "Whether it is a preset template", example = "true") @RequestParam(defaultValue = "true") final boolean preset,
|
||||
@Parameter(description = "List current page", example = "0") @RequestParam(defaultValue = "0") final int pageIndex,
|
||||
@Parameter(description = "Number of list pages", example = "8") @RequestParam(defaultValue = "8") final int pageSize) {
|
||||
Page<NoticeTemplate> templatePage = noticeConfigService.getNoticeTemplates(name, preset, pageIndex, pageSize);
|
||||
return ResponseEntity.ok(Message.success(templatePage));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/templates/all")
|
||||
@Operation(summary = "Get a list of all message notification templates",
|
||||
description = "Get a list of all message notification templates")
|
||||
public ResponseEntity<Message<List<NoticeTemplate>>> getAllTemplates() {
|
||||
return ResponseEntity.ok(Message.success(noticeConfigService.getAllNoticeTemplates()));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/template/{id}")
|
||||
@Operation(summary = "Get the notification template information based on the template ID",
|
||||
description = "Get the notification template information based on the template ID")
|
||||
@@ -18,12 +18,11 @@
|
||||
package org.apache.hertzbeat.alert.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
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
|
||||
@@ -37,31 +36,16 @@ public interface AlertDefineDao extends JpaRepository<AlertDefine, Long>, JpaSpe
|
||||
void deleteAlertDefinesByIdIn(Set<Long> alertDefineIds);
|
||||
|
||||
/**
|
||||
* Query the default alarm thresholds based on the monitoring metrics type
|
||||
* @param app monitoring type
|
||||
* @param metric metrics
|
||||
* Query the alarm define by type
|
||||
* @param type alarm type
|
||||
* @return alarm defines
|
||||
*/
|
||||
List<AlertDefine> queryAlertDefinesByAppAndMetricAndPresetTrueAndEnableTrue(String app, String metric);
|
||||
List<AlertDefine> findAlertDefinesByTypeAndEnableTrue(String type);
|
||||
|
||||
/**
|
||||
* Query app metric alert define
|
||||
* @param app app
|
||||
* @param metric metric
|
||||
* @return alert define
|
||||
* Query the alarm define by name
|
||||
* @param name alarm name
|
||||
* @return alarm 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);
|
||||
Optional<AlertDefine> findAlertDefineByName(String name);
|
||||
}
|
||||
|
||||
+14
-19
@@ -15,36 +15,31 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.dao;
|
||||
package org.apache.hertzbeat.alert.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.hertzbeat.common.entity.manager.TagMonitorBind;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
|
||||
/**
|
||||
* TagMonitorBindDao repository
|
||||
* AlertConverge Dao
|
||||
*/
|
||||
public interface TagMonitorBindDao extends JpaRepository<TagMonitorBind, Long>, JpaSpecificationExecutor<TagMonitorBind> {
|
||||
public interface AlertGroupConvergeDao extends JpaRepository<AlertGroupConverge, Long>, JpaSpecificationExecutor<AlertGroupConverge> {
|
||||
|
||||
/**
|
||||
* delete tags bind by monitor id
|
||||
* @param monitorId monitorId
|
||||
* Delete group alarm converge based on the ID list
|
||||
* @param ids alert converge id list
|
||||
*/
|
||||
void deleteTagMonitorBindsByMonitorId(Long monitorId);
|
||||
@Modifying
|
||||
void deleteAlertGroupConvergesByIdIn(Set<Long> ids);
|
||||
|
||||
/**
|
||||
* delete tags bind by monitor id
|
||||
* @param monitorIds monitor list
|
||||
* Query the enable true group alarm converge list
|
||||
* @return group alarm converge list
|
||||
*/
|
||||
void deleteTagMonitorBindsByMonitorIdIn(Set<Long> monitorIds);
|
||||
|
||||
|
||||
/**
|
||||
* count tags bind relation by tag id
|
||||
* @param tagIds list of tagId
|
||||
* @return count
|
||||
*/
|
||||
long countByTagIdIn(Set<Long> tagIds);
|
||||
|
||||
List<AlertGroupConverge> findAlertGroupConvergesByEnableIsTrue();
|
||||
|
||||
}
|
||||
+14
-6
@@ -17,21 +17,29 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertConverge;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertInhibit;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
|
||||
/**
|
||||
* AlertConverge Dao
|
||||
* AlertInhibit Dao
|
||||
*/
|
||||
public interface AlertConvergeDao extends JpaRepository<AlertConverge, Long>, JpaSpecificationExecutor<AlertConverge> {
|
||||
public interface AlertInhibitDao extends JpaRepository<AlertInhibit, Long>, JpaSpecificationExecutor<AlertInhibit> {
|
||||
|
||||
/**
|
||||
* Delete alarm converge based on the ID list
|
||||
* @param convergeIds alert converge id list
|
||||
* Delete alarm inhibit based on the ID list
|
||||
*
|
||||
* @param ids alert inhibit id list
|
||||
*/
|
||||
@Modifying
|
||||
void deleteAlertConvergesByIdIn(Set<Long> convergeIds);
|
||||
void deleteAlertInhibitsByIdIn(Set<Long> ids);
|
||||
|
||||
/**
|
||||
* Query the enable true alarm inhibit list
|
||||
* @return alarm inhibit list
|
||||
*/
|
||||
List<AlertInhibit> findAlertInhibitsByEnableIsTrue();
|
||||
}
|
||||
@@ -18,8 +18,8 @@
|
||||
package org.apache.hertzbeat.alert.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.apache.hertzbeat.common.entity.manager.Monitor;
|
||||
import org.apache.hertzbeat.common.entity.manager.Tag;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
@@ -36,12 +36,13 @@ public interface AlertMonitorDao extends JpaRepository<Monitor, Long>, JpaSpecif
|
||||
* @return Monitor the list
|
||||
*/
|
||||
List<Monitor> findMonitorsByStatus(Byte status);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* find monitor bind tags by monitorId
|
||||
* @param monitorId monitorId
|
||||
* @return bind tags
|
||||
* Query the monitoring
|
||||
* @param id id
|
||||
* @return monitor
|
||||
*/
|
||||
@Query("select tag from Tag tag join TagMonitorBind bind on bind.tagId = tag.id where bind.monitorId = :monitorId")
|
||||
List<Tag> findMonitorIdBindTags(@Param(value = "monitorId") Long monitorId);
|
||||
@Query("select monitor from Monitor monitor where monitor.id = :id")
|
||||
Optional<Monitor> findMonitorById(@Param(value = "id") Long id);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.dao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertSilence;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
@@ -35,4 +36,10 @@ public interface AlertSilenceDao extends JpaRepository<AlertSilence, Long>, JpaS
|
||||
*/
|
||||
@Modifying
|
||||
void deleteAlertSilencesByIdIn(Set<Long> silenceIds);
|
||||
|
||||
/**
|
||||
* find enabled alert silence
|
||||
* @return enabled alert silence list
|
||||
*/
|
||||
List<AlertSilence> findAlertSilencesByEnableTrue();
|
||||
}
|
||||
|
||||
+21
-14
@@ -17,10 +17,9 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.dao;
|
||||
|
||||
import java.util.HashSet;
|
||||
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.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
@@ -28,29 +27,37 @@ import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
/**
|
||||
* Alert Database Operations
|
||||
* Group Alert Database Operations
|
||||
*/
|
||||
public interface AlertDao extends JpaRepository<Alert, Long>, JpaSpecificationExecutor<Alert> {
|
||||
public interface GroupAlertDao extends JpaRepository<GroupAlert, Long>, JpaSpecificationExecutor<GroupAlert> {
|
||||
|
||||
/**
|
||||
* Query alert group by groupKey
|
||||
* @param groupKey group key identifier
|
||||
* @return alert group
|
||||
*/
|
||||
GroupAlert findByGroupKey(String groupKey);
|
||||
|
||||
/**
|
||||
* Delete alerts based on ID list
|
||||
* @param alertIds Alert ID List
|
||||
* @param ids Alert ID List
|
||||
*/
|
||||
void deleteAlertsByIdIn(Set<Long> alertIds);
|
||||
@Modifying
|
||||
void deleteGroupAlertsByIdIn(HashSet<Long> ids);
|
||||
|
||||
/**
|
||||
* Updates the alarm status based on the alarm ID-status value
|
||||
* @param status status value
|
||||
* @param ids alarm ids
|
||||
* @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("update GroupAlert set status = :status where id in :ids")
|
||||
void updateGroupAlertsStatus(@Param(value = "status") String status, @Param(value = "ids") List<Long> ids);
|
||||
|
||||
/**
|
||||
* Query the number of unhandled alarms of each alarm severity
|
||||
* @return List of alerts num
|
||||
* find group alerts by id list
|
||||
* @param ids ids
|
||||
* @return group alerts
|
||||
*/
|
||||
@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();
|
||||
List<GroupAlert> findGroupAlertsByIdIn(HashSet<Long> ids);
|
||||
}
|
||||
+2
-2
@@ -15,9 +15,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.dao;
|
||||
package org.apache.hertzbeat.alert.dao;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
+2
-2
@@ -15,10 +15,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.dao;
|
||||
package org.apache.hertzbeat.alert.dao;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
+2
-2
@@ -15,9 +15,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.dao;
|
||||
package org.apache.hertzbeat.alert.dao;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.HashSet;
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Single Alert Database Operations
|
||||
*/
|
||||
public interface SingleAlertDao extends JpaRepository<SingleAlert, Long>, JpaSpecificationExecutor<SingleAlert> {
|
||||
|
||||
/**
|
||||
* Query alert by fingerprint
|
||||
* @param fingerprint alert fingerprint
|
||||
* @return alert
|
||||
*/
|
||||
SingleAlert findByFingerprint(String fingerprint);
|
||||
|
||||
/**
|
||||
* Query alerts by fingerprint list
|
||||
* @param fingerprints alert fingerprint list
|
||||
* @return alerts
|
||||
*/
|
||||
List<SingleAlert> findSingleAlertsByFingerprintIn(List<String> fingerprints);
|
||||
|
||||
/**
|
||||
* Query alerts by status
|
||||
* @param status status firing or resolved
|
||||
* @return alerts
|
||||
*/
|
||||
List<SingleAlert> querySingleAlertsByStatus(String status);
|
||||
|
||||
/**
|
||||
* Delete alerts based on ID list
|
||||
* @param ids Alert ID List
|
||||
*/
|
||||
@Modifying
|
||||
void deleteSingleAlertsByIdIn(HashSet<Long> ids);
|
||||
|
||||
/**
|
||||
* Updates the alarm status based on the alarm ID-status value
|
||||
* @param status status value
|
||||
* @param ids alarm ids
|
||||
*/
|
||||
@Modifying
|
||||
@Query("update SingleAlert set status = :status where id in :ids")
|
||||
void updateSingleAlertsStatus(@Param(value = "status") String status, @Param(value = "ids") List<Long> ids);
|
||||
|
||||
/**
|
||||
* delete alerts by fingerprint list
|
||||
* @param firingAlerts fingerprint list
|
||||
*/
|
||||
@Modifying
|
||||
void deleteSingleAlertsByFingerprintIn(List<String> firingAlerts);
|
||||
}
|
||||
+13
-18
@@ -21,9 +21,8 @@ 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 java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import org.apache.hertzbeat.common.entity.manager.TagItem;
|
||||
|
||||
/**
|
||||
* Data transfer object for alert configuration
|
||||
@@ -34,26 +33,22 @@ import org.apache.hertzbeat.common.entity.manager.TagItem;
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@ExcelTarget(value = "AlertDefineDTO")
|
||||
public 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 = "Name")
|
||||
private String name;
|
||||
@Excel(name = "Type")
|
||||
private String type;
|
||||
@Excel(name = "Expr")
|
||||
private String expr;
|
||||
@Excel(name = "Priority")
|
||||
private Byte priority;
|
||||
@Excel(name = "Period")
|
||||
private Integer period;
|
||||
@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 = "Labels")
|
||||
private Map<String, String> labels;
|
||||
@Excel(name = "Annotations")
|
||||
private Map<String, String> annotations;
|
||||
@Excel(name = "Template")
|
||||
private String template;
|
||||
@Excel(name = "Enable")
|
||||
private Boolean enable;
|
||||
}
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.List;
|
||||
import java.util.Map;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* AlertManagerExternAlert
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class AlertManagerExternAlert {
|
||||
|
||||
private String groupKey;
|
||||
|
||||
private String externalURL;
|
||||
|
||||
private String version;
|
||||
|
||||
private String status;
|
||||
|
||||
private Map<String, String> groupLabels;
|
||||
|
||||
private Map<String, String> commonLabels;
|
||||
|
||||
private Map<String, String> commonAnnotations;
|
||||
|
||||
private List<PrometheusExternAlert> alerts;
|
||||
}
|
||||
-78
@@ -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();
|
||||
|
||||
}
|
||||
-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.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");
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.pojo.dto;
|
||||
package org.apache.hertzbeat.alert.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.Max;
|
||||
@@ -32,7 +32,7 @@ import lombok.NoArgsConstructor;
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EmailNoticeSender {
|
||||
public class MailServerConfig {
|
||||
|
||||
@NotNull(message = "Type cannot be empty")
|
||||
private Integer type;
|
||||
+18
-11
@@ -15,27 +15,34 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.common.entity.manager;
|
||||
package org.apache.hertzbeat.alert.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
|
||||
/**
|
||||
* tag item
|
||||
* Prometheus Alert Content Entity
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class TagItem {
|
||||
public class PrometheusExternAlert {
|
||||
|
||||
private Map<String, String> labels;
|
||||
|
||||
private Map<String, String> annotations;
|
||||
|
||||
@Schema(title = "Tag Name")
|
||||
@NotBlank
|
||||
private String name;
|
||||
private String status;
|
||||
|
||||
@Schema(title = "Tag Value")
|
||||
private String value;
|
||||
private Instant startsAt;
|
||||
|
||||
private Instant endsAt;
|
||||
|
||||
private String generatorURL;
|
||||
}
|
||||
-227
@@ -1,227 +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();
|
||||
// Check Type.
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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 lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
*
|
||||
* Tencent cloud alarm entity class
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class TencentCloudExternAlert {
|
||||
@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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transaction alarm
|
||||
*/
|
||||
private static final String EVENT = "event";
|
||||
|
||||
/**
|
||||
* Indicator alarm
|
||||
*/
|
||||
private static final String METRIC = "metric";
|
||||
|
||||
}
|
||||
-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);
|
||||
}
|
||||
|
||||
}
|
||||
+41
-74
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter;
|
||||
package org.apache.hertzbeat.alert.notice;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.List;
|
||||
@@ -23,18 +23,16 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.AlerterWorkerPool;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.queue.CommonDataQueue;
|
||||
import org.apache.hertzbeat.manager.service.NoticeConfigService;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.apache.hertzbeat.manager.support.exception.IgnoreException;
|
||||
import org.apache.hertzbeat.alert.config.AlertSseManager;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.alert.service.NoticeConfigService;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.hertzbeat.plugin.PostAlertPlugin;
|
||||
import org.apache.hertzbeat.plugin.Plugin;
|
||||
import org.apache.hertzbeat.plugin.runner.PluginRunner;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
@@ -42,40 +40,28 @@ import org.springframework.stereotype.Component;
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class DispatcherAlarm implements InitializingBean {
|
||||
|
||||
private static final int DISPATCH_THREADS = 3;
|
||||
public class AlertNoticeDispatch {
|
||||
|
||||
private final AlerterWorkerPool workerPool;
|
||||
private final CommonDataQueue dataQueue;
|
||||
private final NoticeConfigService noticeConfigService;
|
||||
private final AlertStoreHandler alertStoreHandler;
|
||||
private final Map<Byte, AlertNotifyHandler> alertNotifyHandlerMap;
|
||||
private final PluginRunner pluginRunner;
|
||||
private final AlertSseManager emitterManager;
|
||||
|
||||
public DispatcherAlarm(AlerterWorkerPool workerPool,
|
||||
CommonDataQueue dataQueue,
|
||||
NoticeConfigService noticeConfigService,
|
||||
AlertStoreHandler alertStoreHandler,
|
||||
List<AlertNotifyHandler> alertNotifyHandlerList, PluginRunner pluginRunner) {
|
||||
public AlertNoticeDispatch(AlerterWorkerPool workerPool,
|
||||
NoticeConfigService noticeConfigService,
|
||||
AlertStoreHandler alertStoreHandler,
|
||||
List<AlertNotifyHandler> alertNotifyHandlerList, PluginRunner pluginRunner, AlertSseManager emitterManager) {
|
||||
this.workerPool = workerPool;
|
||||
this.dataQueue = dataQueue;
|
||||
this.noticeConfigService = noticeConfigService;
|
||||
this.alertStoreHandler = alertStoreHandler;
|
||||
this.pluginRunner = pluginRunner;
|
||||
alertNotifyHandlerMap = Maps.newHashMapWithExpectedSize(alertNotifyHandlerList.size());
|
||||
this.emitterManager = emitterManager;
|
||||
alertNotifyHandlerList.forEach(r -> alertNotifyHandlerMap.put(r.type(), r));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
// Start alarm distribution
|
||||
DispatchTask dispatchTask = new DispatchTask();
|
||||
for (int i = 0; i < DISPATCH_THREADS; i++) {
|
||||
workerPool.executeJob(dispatchTask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* send alert msg to receiver
|
||||
*
|
||||
@@ -83,7 +69,7 @@ public class DispatcherAlarm implements InitializingBean {
|
||||
* @param alert alert msg
|
||||
* @return send success or failed
|
||||
*/
|
||||
public boolean sendNoticeMsg(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) {
|
||||
public boolean sendNoticeMsg(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
|
||||
if (receiver == null || receiver.getType() == null) {
|
||||
log.warn("DispatcherAlarm-sendNoticeMsg params is empty alert:[{}], receiver:[{}]", alert, receiver);
|
||||
return false;
|
||||
@@ -115,53 +101,34 @@ public class DispatcherAlarm implements InitializingBean {
|
||||
return noticeConfigService.getOneTemplateById(id);
|
||||
}
|
||||
|
||||
private Optional<List<NoticeRule>> matchNoticeRulesByAlert(Alert alert) {
|
||||
private Optional<List<NoticeRule>> matchNoticeRulesByAlert(GroupAlert alert) {
|
||||
return Optional.ofNullable(noticeConfigService.getReceiverFilterRule(alert));
|
||||
}
|
||||
|
||||
private class DispatchTask implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
try {
|
||||
Alert alert = dataQueue.pollAlertsData();
|
||||
if (alert != null) {
|
||||
// Determining alarm type storage
|
||||
alertStoreHandler.store(alert);
|
||||
// Notice distribution
|
||||
sendNotify(alert);
|
||||
// Execute the plugin if enable (Compatible with old version plugins, will be removed in later versions)
|
||||
pluginRunner.pluginExecute(Plugin.class, plugin -> plugin.alert(alert));
|
||||
// Execute the plugin if enable with params
|
||||
pluginRunner.pluginExecute(PostAlertPlugin.class, (afterAlertPlugin, pluginContext) -> afterAlertPlugin.execute(alert, pluginContext));
|
||||
|
||||
}
|
||||
} catch (IgnoreException ignored) {
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error(e.getMessage());
|
||||
} catch (Exception exception) {
|
||||
log.error(exception.getMessage(), exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendNotify(Alert alert) {
|
||||
matchNoticeRulesByAlert(alert).ifPresent(noticeRules -> noticeRules.forEach(rule -> {
|
||||
workerPool.executeNotify(() -> {
|
||||
rule.getReceiverId()
|
||||
.forEach(receiverId -> {
|
||||
try {
|
||||
sendNoticeMsg(getOneReceiverById(receiverId),
|
||||
getOneTemplateById(rule.getTemplateId()), alert);
|
||||
} catch (AlertNoticeException e) {
|
||||
log.warn("DispatchTask sendNoticeMsg error, message: {}", e.getMessage());
|
||||
}
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
public void dispatchAlarm(GroupAlert groupAlert) {
|
||||
if (groupAlert != null) {
|
||||
// Determining alarm type storage
|
||||
GroupAlert storedGroupAlert = alertStoreHandler.store(groupAlert);
|
||||
// Notice distribution
|
||||
sendNotify(storedGroupAlert);
|
||||
// Execute the plugin if enable (Compatible with old version plugins, will be removed in later versions)
|
||||
pluginRunner.pluginExecute(Plugin.class, plugin -> plugin.alert(storedGroupAlert));
|
||||
// Execute the plugin if enable with params
|
||||
pluginRunner.pluginExecute(PostAlertPlugin.class, (afterAlertPlugin, pluginContext) -> afterAlertPlugin.execute(storedGroupAlert, pluginContext));
|
||||
// Send alert to the sse client
|
||||
emitterManager.broadcast(JsonUtil.toJson(storedGroupAlert));
|
||||
}
|
||||
}
|
||||
|
||||
private void sendNotify(GroupAlert alert) {
|
||||
matchNoticeRulesByAlert(alert).ifPresent(noticeRules -> noticeRules.forEach(rule -> workerPool.executeNotify(() -> rule.getReceiverId()
|
||||
.forEach(receiverId -> {
|
||||
try {
|
||||
sendNoticeMsg(getOneReceiverById(receiverId),
|
||||
getOneTemplateById(rule.getTemplateId()), alert);
|
||||
} catch (AlertNoticeException e) {
|
||||
log.warn("DispatchTask sendNoticeMsg error, message: {}", e.getMessage());
|
||||
}
|
||||
}))));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.support.exception;
|
||||
package org.apache.hertzbeat.alert.notice;
|
||||
|
||||
/**
|
||||
* alert notice send failed
|
||||
+5
-6
@@ -15,12 +15,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter;
|
||||
package org.apache.hertzbeat.alert.notice;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
|
||||
/**
|
||||
* AlertNotifyHandler interface
|
||||
@@ -34,7 +33,7 @@ public interface AlertNotifyHandler {
|
||||
* @param alert Alarm information
|
||||
* @throws AlertNoticeException when send receiver error
|
||||
*/
|
||||
void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) throws AlertNoticeException;
|
||||
void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) throws AlertNoticeException;
|
||||
|
||||
/**
|
||||
* notification type
|
||||
+5
-3
@@ -15,9 +15,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter;
|
||||
package org.apache.hertzbeat.alert.notice;
|
||||
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
|
||||
/**
|
||||
* Alarm persistence
|
||||
@@ -28,7 +28,9 @@ public interface AlertStoreHandler {
|
||||
* Persistent alarm records
|
||||
* It is necessary to associate and assign values
|
||||
* to the alert tag information tags while persisting.
|
||||
*
|
||||
* @param alert alarm information
|
||||
* @return groupAlert
|
||||
*/
|
||||
void store(Alert alert);
|
||||
GroupAlert store(GroupAlert alert);
|
||||
}
|
||||
+12
-54
@@ -15,29 +15,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import freemarker.cache.StringTemplateLoader;
|
||||
import freemarker.core.TemplateClassResolver;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.TemplateException;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import javax.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.AlerterProperties;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.support.event.SystemConfigChangeEvent;
|
||||
import org.apache.hertzbeat.common.util.ResourceBundleUtil;
|
||||
import org.apache.hertzbeat.manager.component.alerter.AlertNotifyHandler;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNotifyHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
@@ -49,15 +45,14 @@ import org.springframework.web.client.RestTemplate;
|
||||
abstract class AbstractAlertNotifyHandlerImpl implements AlertNotifyHandler {
|
||||
|
||||
private static final String NUMBER_FORMAT = "0";
|
||||
protected static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
protected ResourceBundle bundle = ResourceBundleUtil.getBundle("alerter");
|
||||
@Resource
|
||||
@Autowired
|
||||
protected RestTemplate restTemplate;
|
||||
@Resource
|
||||
@Autowired
|
||||
protected AlerterProperties alerterProperties;
|
||||
|
||||
|
||||
protected String renderContent(NoticeTemplate noticeTemplate, Alert alert) throws TemplateException, IOException {
|
||||
protected String renderContent(NoticeTemplate noticeTemplate, GroupAlert alert) throws TemplateException, IOException {
|
||||
StringTemplateLoader stringLoader = new StringTemplateLoader();
|
||||
freemarker.template.Template templateRes;
|
||||
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
|
||||
@@ -65,51 +60,14 @@ abstract class AbstractAlertNotifyHandlerImpl implements AlertNotifyHandler {
|
||||
cfg.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);
|
||||
Map<String, Object> model = new HashMap<>(16);
|
||||
model.put("title", bundle.getString("alerter.notify.title"));
|
||||
|
||||
if (alert.getTags() != null) {
|
||||
String monitorId = alert.getTags().get(CommonConstants.TAG_MONITOR_ID);
|
||||
if (monitorId != null) {
|
||||
model.put("monitorId", monitorId);
|
||||
}
|
||||
String monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME);
|
||||
if (monitorName != null) {
|
||||
model.put("monitorName", monitorName);
|
||||
}
|
||||
String monitorHost = alert.getTags().get(CommonConstants.TAG_MONITOR_HOST);
|
||||
if (monitorHost != null) {
|
||||
model.put("monitorHost", monitorHost);
|
||||
}
|
||||
String thresholdId = alert.getTags().get(CommonConstants.TAG_THRESHOLD_ID);
|
||||
if (thresholdId != null) {
|
||||
model.put("thresholdId", thresholdId);
|
||||
}
|
||||
}
|
||||
model.put("alarmId", alert.getId());
|
||||
model.put("status", alert.getStatus());
|
||||
model.put("monitorIdLabel", bundle.getString("alerter.notify.monitorId"));
|
||||
model.put("monitorNameLabel", bundle.getString("alerter.notify.monitorName"));
|
||||
model.put("monitorHostLabel", bundle.getString("alerter.notify.monitorHost"));
|
||||
model.put("target", alert.getTarget());
|
||||
model.put("targetLabel", bundle.getString("alerter.notify.target"));
|
||||
model.put("priorityLabel", bundle.getString("alerter.notify.priority"));
|
||||
model.put("priority", bundle.getString("alerter.priority." + alert.getPriority()));
|
||||
model.put("priorityValue", alert.getPriority());
|
||||
model.put("triggerTimeLabel", bundle.getString("alerter.notify.triggerTime"));
|
||||
model.put("triggerTime", DTF.format(Instant.ofEpochMilli(alert.getLastAlarmTime()).atZone(ZoneId.systemDefault()).toLocalDateTime()));
|
||||
if (CommonConstants.ALERT_STATUS_CODE_RESTORED == alert.getStatus()) {
|
||||
model.put("restoreTimeLabel", bundle.getString("alerter.notify.restoreTime"));
|
||||
model.put("restoreTime", DTF.format(Instant.ofEpochMilli(alert.getFirstAlarmTime()).atZone(ZoneId.systemDefault()).toLocalDateTime()));
|
||||
}
|
||||
model.put("timesLabel", bundle.getString("alerter.notify.times"));
|
||||
model.put("times", alert.getTimes());
|
||||
model.put("contentLabel", bundle.getString("alerter.notify.content"));
|
||||
model.put("content", alert.getContent());
|
||||
model.put("tagsLabel", bundle.getString("alerter.notify.tags"));
|
||||
model.put("tags", alert.getTags());
|
||||
model.put("groupLabels", alert.getGroupLabels());
|
||||
model.put("commonLabels", alert.getCommonLabels());
|
||||
model.put("commonAnnotations", alert.getCommonAnnotations());
|
||||
model.put("alerts", alert.getAlerts());
|
||||
if (alerterProperties != null) {
|
||||
model.put("consoleUrl", alerterProperties.getConsoleUrl());
|
||||
}
|
||||
model.put("consoleLabel", bundle.getString("alerter.notify.console"));
|
||||
// TODO Single instance reuse cache considers multiple-threading issues
|
||||
String templateName = "freeMakerTemplate";
|
||||
stringLoader.putTemplate(templateName, noticeTemplate.getContent());
|
||||
+6
-15
@@ -15,17 +15,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.util.ResourceBundleUtil;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -41,17 +40,9 @@ final class AliYunAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
|
||||
private final ResourceBundle bundle = ResourceBundleUtil.getBundle("alerter");
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
|
||||
// SMS notification
|
||||
try {
|
||||
String monitorName = null;
|
||||
if (alert.getTags() != null) {
|
||||
monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME);
|
||||
}
|
||||
String[] params = new String[3];
|
||||
params[0] = monitorName == null ? alert.getTarget() : monitorName;
|
||||
params[1] = bundle.getString("alerter.priority." + alert.getPriority());
|
||||
params[2] = alert.getContent();
|
||||
// todo send aliyun sms
|
||||
} catch (Exception e) {
|
||||
throw new AlertNoticeException("[Sms Notify Error] " + e.getMessage());
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.notice.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dao.GroupAlertDao;
|
||||
import org.apache.hertzbeat.alert.dao.SingleAlertDao;
|
||||
import org.apache.hertzbeat.alert.notice.AlertStoreHandler;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Alarm data persistence - landing in the database
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
final class DbAlertStoreHandlerImpl implements AlertStoreHandler {
|
||||
|
||||
private final GroupAlertDao groupAlertDao;
|
||||
|
||||
private final SingleAlertDao singleAlertDao;
|
||||
|
||||
@Override
|
||||
public GroupAlert store(GroupAlert groupAlert) {
|
||||
if (groupAlert == null || groupAlert.getAlerts() == null || groupAlert.getAlerts().isEmpty()) {
|
||||
log.error("The Group Alerts is empty, ignore store");
|
||||
return groupAlert;
|
||||
}
|
||||
// 1. Find existing alert group
|
||||
GroupAlert existGroupAlert = groupAlertDao.findByGroupKey(groupAlert.getGroupKey());
|
||||
|
||||
// 2. Process individual alerts
|
||||
Set<String> alertFingerprints = new HashSet<>(8);
|
||||
|
||||
List<SingleAlert> originalAlerts = groupAlert.getAlerts();
|
||||
List<SingleAlert> newAlerts = new ArrayList<>();
|
||||
|
||||
|
||||
for (SingleAlert singleAlert : originalAlerts) {
|
||||
SingleAlert existAlert = singleAlertDao.findByFingerprint(singleAlert.getFingerprint());
|
||||
|
||||
if (existAlert != null) {
|
||||
// Update the existing alert with the ID and creation time from the database
|
||||
singleAlert.setId(existAlert.getId());
|
||||
singleAlert.setGmtCreate(existAlert.getGmtCreate());
|
||||
|
||||
// Status transition logic
|
||||
if (CommonConstants.ALERT_STATUS_FIRING.equals(singleAlert.getStatus())) {
|
||||
// If the alert is firing and the existing alert is not resolved, update the start time and trigger times
|
||||
if (!CommonConstants.ALERT_STATUS_RESOLVED.equals(existAlert.getStatus())) {
|
||||
singleAlert.setStartAt(existAlert.getStartAt());
|
||||
int triggerTimes = Optional.ofNullable(existAlert.getTriggerTimes()).orElse(1)
|
||||
+ Optional.ofNullable(singleAlert.getTriggerTimes()).orElse(1);
|
||||
singleAlert.setTriggerTimes(triggerTimes);
|
||||
}
|
||||
} else if (CommonConstants.ALERT_STATUS_RESOLVED.equals(singleAlert.getStatus())) {
|
||||
// If the alert is resolved, set the end time (if not already set) and copy other fields from the existing alert
|
||||
if (singleAlert.getEndAt() == null) {
|
||||
singleAlert.setEndAt(System.currentTimeMillis());
|
||||
}
|
||||
singleAlert.setStartAt(existAlert.getStartAt());
|
||||
singleAlert.setActiveAt(existAlert.getActiveAt());
|
||||
singleAlert.setTriggerTimes(existAlert.getTriggerTimes());
|
||||
}
|
||||
}
|
||||
SingleAlert savedSingleAlert = singleAlertDao.save(singleAlert);
|
||||
newAlerts.add(savedSingleAlert);
|
||||
alertFingerprints.add(savedSingleAlert.getFingerprint());
|
||||
}
|
||||
groupAlert.setAlerts(newAlerts);
|
||||
// 3. Process resolved alerts
|
||||
if (existGroupAlert != null) {
|
||||
List<String> existFingerprints = existGroupAlert.getAlertFingerprints();
|
||||
if (existFingerprints != null) {
|
||||
alertFingerprints.addAll(existFingerprints);
|
||||
}
|
||||
// Merge group information
|
||||
groupAlert.setId(existGroupAlert.getId());
|
||||
groupAlert.setGmtCreate(existGroupAlert.getGmtCreate());
|
||||
// Merge other historical information to preserve
|
||||
Map<String, String> existCommonLabels = existGroupAlert.getCommonLabels();
|
||||
if (existCommonLabels != null) {
|
||||
Map<String, String> commonLabels = groupAlert.getCommonLabels();
|
||||
if (commonLabels != null) {
|
||||
// filter common label in commonLabels and existCommonLabels
|
||||
commonLabels = commonLabels.entrySet().stream()
|
||||
.filter(entry -> existCommonLabels.containsKey(entry.getKey()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
groupAlert.setCommonLabels(commonLabels);
|
||||
}
|
||||
}
|
||||
Map<String, String> existCommonAnnotations = existGroupAlert.getCommonAnnotations();
|
||||
if (existCommonAnnotations != null) {
|
||||
Map<String, String> commonAnnotations = groupAlert.getCommonAnnotations();
|
||||
if (commonAnnotations != null) {
|
||||
// filter common annotation in commonAnnotations and existCommonAnnotations
|
||||
commonAnnotations = commonAnnotations.entrySet().stream()
|
||||
.filter(entry -> existCommonAnnotations.containsKey(entry.getKey()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
groupAlert.setCommonAnnotations(commonAnnotations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Save alert group
|
||||
groupAlert.setAlertFingerprints(alertFingerprints.stream().toList());
|
||||
GroupAlert savedGroupAlert = groupAlertDao.save(groupAlert);
|
||||
savedGroupAlert.setAlerts(groupAlert.getAlerts());
|
||||
return savedGroupAlert;
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.List;
|
||||
@@ -23,11 +23,11 @@ import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.util.StrUtil;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -44,7 +44,7 @@ import org.springframework.stereotype.Component;
|
||||
final class DingTalkRobotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl {
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
|
||||
try {
|
||||
DingTalkWebHookDto dingTalkWebHookDto = new DingTalkWebHookDto();
|
||||
DingTalkWebHookDto.MarkdownDTO markdownDTO = new DingTalkWebHookDto.MarkdownDTO();
|
||||
+7
-7
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import java.util.List;
|
||||
@@ -23,10 +23,10 @@ import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -42,7 +42,7 @@ import org.springframework.stereotype.Component;
|
||||
final class DiscordBotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl {
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) throws AlertNoticeException {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) throws AlertNoticeException {
|
||||
try {
|
||||
var notifyBody = DiscordNotifyDTO.builder()
|
||||
.embeds(List.of(EmbedDTO.builder()
|
||||
@@ -93,7 +93,7 @@ final class DiscordBotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerI
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
private static class DiscordResponseDTO {
|
||||
public static class DiscordResponseDTO {
|
||||
private String id;
|
||||
private Integer type;
|
||||
private String content;
|
||||
+9
-9
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
@@ -24,15 +24,15 @@ import java.util.Properties;
|
||||
import java.util.ResourceBundle;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.alert.dto.MailServerConfig;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.manager.GeneralConfig;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.support.event.SystemConfigChangeEvent;
|
||||
import org.apache.hertzbeat.common.util.ResourceBundleUtil;
|
||||
import org.apache.hertzbeat.manager.dao.GeneralConfigDao;
|
||||
import org.apache.hertzbeat.manager.pojo.dto.EmailNoticeSender;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.apache.hertzbeat.base.dao.GeneralConfigDao;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
@@ -77,7 +77,7 @@ public class EmailAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
|
||||
private ResourceBundle bundle = ResourceBundleUtil.getBundle("alerter");
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) throws AlertNoticeException {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) throws AlertNoticeException {
|
||||
try {
|
||||
// get sender
|
||||
JavaMailSenderImpl sender = (JavaMailSenderImpl) javaMailSender;
|
||||
@@ -88,7 +88,7 @@ public class EmailAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
|
||||
if (emailConfig != null && emailConfig.getContent() != null) {
|
||||
// enable database configuration
|
||||
String content = emailConfig.getContent();
|
||||
EmailNoticeSender emailNoticeSenderConfig = objectMapper.readValue(content, EmailNoticeSender.class);
|
||||
MailServerConfig emailNoticeSenderConfig = objectMapper.readValue(content, MailServerConfig.class);
|
||||
if (emailNoticeSenderConfig.isEnable()) {
|
||||
sender.setHost(emailNoticeSenderConfig.getEmailHost());
|
||||
sender.setPort(emailNoticeSenderConfig.getEmailPort());
|
||||
+8
-7
@@ -15,18 +15,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -49,10 +49,11 @@ final class FlyBookAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
|
||||
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
|
||||
try {
|
||||
String notificationContent = JsonUtil.toJson(renderContent(noticeTemplate, alert));
|
||||
String cardMessage = createLarkMessage(receiver.getUserId(), notificationContent, alert.getPriority());
|
||||
// todo priority custom the color
|
||||
String cardMessage = createLarkMessage(receiver.getUserId(), notificationContent, (byte) 1);
|
||||
String webHookUrl = alerterProperties.getFlyBookWebhookUrl() + receiver.getAccessToken();
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
+6
-6
@@ -15,16 +15,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -49,7 +49,7 @@ public class GotifyAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
|
||||
* @throws AlertNoticeException when send receiver error
|
||||
*/
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) throws AlertNoticeException {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) throws AlertNoticeException {
|
||||
try {
|
||||
GotifyWebHookDto gotifyWebHookDto = new GotifyWebHookDto();
|
||||
gotifyWebHookDto.setTitle(bundle.getString("alerter.notify.title"));
|
||||
+8
-6
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import com.huaweicloud.sdk.core.auth.BasicCredentials;
|
||||
import com.huaweicloud.sdk.smn.v2.SmnClient;
|
||||
@@ -27,11 +27,11 @@ import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.util.ResourceBundleUtil;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
@@ -40,13 +40,15 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Deprecated
|
||||
final class HuaweiCloudSmnAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl {
|
||||
private final ResourceBundle bundle = ResourceBundleUtil.getBundle("alerter");
|
||||
|
||||
private final Map<String, SmnClient> smnClientMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
|
||||
// todo use rest api not sdk
|
||||
try {
|
||||
var smnClient = getSmnClient(receiver);
|
||||
var request = new PublishMessageRequest()
|
||||
+6
-6
@@ -15,15 +15,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -46,7 +46,7 @@ public class ServerChanAlertNotifyHandlerImpl extends AbstractAlertNotifyHandler
|
||||
* @throws AlertNoticeException when send receiver error
|
||||
*/
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) throws AlertNoticeException {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) throws AlertNoticeException {
|
||||
try {
|
||||
ServerChanAlertNotifyHandlerImpl.ServerChanWebHookDto serverChanWebHookDto = new ServerChanAlertNotifyHandlerImpl.ServerChanWebHookDto();
|
||||
serverChanWebHookDto.setTitle(bundle.getString("alerter.notify.title"));
|
||||
+6
-6
@@ -15,17 +15,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import java.util.Objects;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -44,7 +44,7 @@ final class SlackAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl {
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) throws AlertNoticeException {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) throws AlertNoticeException {
|
||||
try {
|
||||
var slackNotify = SlackNotifyDTO.builder()
|
||||
.text(renderContent(noticeTemplate, alert))
|
||||
+23
-15
@@ -15,18 +15,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import java.util.ResourceBundle;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.alert.service.TencentSmsClient;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.util.ResourceBundleUtil;
|
||||
import org.apache.hertzbeat.manager.service.TencentSmsClient;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -37,6 +36,7 @@ import org.springframework.stereotype.Component;
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@ConditionalOnProperty("common.sms.tencent.app-id")
|
||||
@Deprecated
|
||||
final class SmsAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl {
|
||||
|
||||
private final TencentSmsClient tencentSmsClient;
|
||||
@@ -44,17 +44,25 @@ final class SmsAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl {
|
||||
private final ResourceBundle bundle = ResourceBundleUtil.getBundle("alerter");
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) {
|
||||
// SMS notification
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
|
||||
// SMS notification todo use the rest api not sdk
|
||||
try {
|
||||
String monitorName = null;
|
||||
if (alert.getTags() != null) {
|
||||
monitorName = alert.getTags().get(CommonConstants.TAG_MONITOR_NAME);
|
||||
String instance = null;
|
||||
String priority = null;
|
||||
String content = null;
|
||||
if (alert.getCommonLabels() != null) {
|
||||
instance = alert.getCommonLabels().get("instance");
|
||||
priority = alert.getCommonLabels().get("priority");
|
||||
content = alert.getCommonAnnotations().get("summary");
|
||||
content = content == null ? alert.getCommonAnnotations().get("description") : content;
|
||||
if (content == null) {
|
||||
content = alert.getCommonAnnotations().values().stream().findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
String[] params = new String[3];
|
||||
params[0] = monitorName == null ? alert.getTarget() : monitorName;
|
||||
params[1] = bundle.getString("alerter.priority." + alert.getPriority());
|
||||
params[2] = alert.getContent();
|
||||
params[0] = instance == null ? alert.getGroupKey() : instance;
|
||||
params[1] = priority == null ? "unknown" : priority;
|
||||
params[2] = content;
|
||||
tencentSmsClient.sendMessage(params, new String[]{receiver.getPhone()});
|
||||
} catch (Exception e) {
|
||||
throw new AlertNoticeException("[Sms Notify Error] " + e.getMessage());
|
||||
+7
-7
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
@@ -23,10 +23,10 @@ import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -42,7 +42,7 @@ import org.springframework.stereotype.Component;
|
||||
final class TelegramBotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl {
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) throws AlertNoticeException {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) throws AlertNoticeException {
|
||||
try {
|
||||
String url = String.format(alerterProperties.getTelegramWebhookUrl(), receiver.getTgBotToken());
|
||||
TelegramBotNotifyDTO notifyBody = TelegramBotNotifyDTO.builder()
|
||||
@@ -89,7 +89,7 @@ final class TelegramBotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandler
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
private static class TelegramBotNotifyResponse {
|
||||
public static class TelegramBotNotifyResponse {
|
||||
private boolean ok;
|
||||
@JsonProperty("error_code")
|
||||
private Integer errorCode;
|
||||
+8
-7
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
@@ -23,9 +23,9 @@ import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -44,7 +44,7 @@ final class WeChatAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
|
||||
private static final String ACCESS_TOKEN = "access_token";
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
|
||||
try {
|
||||
String accessToken = getAccessToken();
|
||||
String messageContent = constructMessageContent(receiver, noticeTemplate, alert);
|
||||
@@ -73,14 +73,15 @@ final class WeChatAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
private String constructMessageContent(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) {
|
||||
private String constructMessageContent(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
|
||||
// example: construct a text message content
|
||||
JsonObject messageContent = new JsonObject();
|
||||
messageContent.addProperty("msgtype", "text");
|
||||
JsonObject textContent = new JsonObject();
|
||||
|
||||
// Here you can construct the message content based on the NoticeTemplate and Alert information
|
||||
String alertMessage = String.format("警告:%s\n详情:%s", alert.getAlertDefineId(), alert.getContent());
|
||||
// String alertMessage = String.format("警告:%s\n详情:%s", alert.getAlertDefineId(), alert.getContent());
|
||||
String alertMessage = "Alert message content";
|
||||
textContent.addProperty("content", alertMessage);
|
||||
messageContent.add("text", textContent);
|
||||
|
||||
+18
-6
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.Objects;
|
||||
@@ -25,10 +25,10 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -62,7 +62,7 @@ public class WeComAppAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerIm
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) throws AlertNoticeException {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) throws AlertNoticeException {
|
||||
String corpId = receiver.getCorpId();
|
||||
Integer agentId = receiver.getAgentId();
|
||||
String appSecret = receiver.getAppSecret();
|
||||
@@ -120,6 +120,9 @@ public class WeComAppAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerIm
|
||||
return 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* WeChat app request
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@@ -135,6 +138,9 @@ public class WeComAppAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerIm
|
||||
private String accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* WeChat app message
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@@ -171,6 +177,9 @@ public class WeComAppAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerIm
|
||||
*/
|
||||
private MarkdownDTO markdown;
|
||||
|
||||
/**
|
||||
* markdown message
|
||||
*/
|
||||
@Data
|
||||
protected static class MarkdownDTO {
|
||||
/**
|
||||
@@ -179,6 +188,9 @@ public class WeComAppAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerIm
|
||||
private String content;
|
||||
}
|
||||
|
||||
/**
|
||||
* text message
|
||||
*/
|
||||
@Data
|
||||
protected static class TextDTO {
|
||||
/**
|
||||
+7
-7
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.List;
|
||||
@@ -27,11 +27,11 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.util.StrUtil;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -48,7 +48,7 @@ import org.springframework.stereotype.Component;
|
||||
final class WeComRobotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl {
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
|
||||
try {
|
||||
WeWorkWebHookDto weWorkWebHookDTO = new WeWorkWebHookDto();
|
||||
WeWorkWebHookDto.MarkdownDTO markdownDTO = new WeWorkWebHookDto.MarkdownDTO();
|
||||
@@ -82,7 +82,7 @@ final class WeComRobotAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerI
|
||||
}
|
||||
}
|
||||
|
||||
private WeWorkWebHookDto checkNeedAtNominator(NoticeReceiver receiver, Alert alert) {
|
||||
private WeWorkWebHookDto checkNeedAtNominator(NoticeReceiver receiver, GroupAlert alert) {
|
||||
if (StringUtils.isBlank(receiver.getPhone()) && StringUtils.isBlank(receiver.getUserId())) {
|
||||
return null;
|
||||
}
|
||||
+7
-34
@@ -15,16 +15,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.component.alerter.impl;
|
||||
package org.apache.hertzbeat.alert.notice.impl;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.manager.support.exception.AlertNoticeException;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeException;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -40,14 +37,11 @@ import org.springframework.stereotype.Component;
|
||||
final class WebHookAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl {
|
||||
|
||||
@Override
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, Alert alert) {
|
||||
public void send(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
|
||||
// fix null pointer exception
|
||||
filterInvalidTags(alert);
|
||||
alert.setContent(escapeJsonStr(alert.getContent()));
|
||||
// alert.setContent(escapeJsonStr(alert.getContent()));
|
||||
String webhookJson = renderContent(noticeTemplate, alert);
|
||||
webhookJson = webhookJson.replace(",\n }", "\n }");
|
||||
|
||||
@@ -68,25 +62,4 @@ final class WebHookAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
|
||||
public byte type() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
private void filterInvalidTags(Alert alert) {
|
||||
if (alert.getTags() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<Map.Entry<String, String>> iterator = alert.getTags().entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, String> entry = iterator.next();
|
||||
if (StringUtils.isNoneBlank(entry.getKey(), entry.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
// In order to beautify Freemarker template
|
||||
if (alert.getTags().entrySet().size() <= 0L) {
|
||||
alert.setTags(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
-41
@@ -17,57 +17,85 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.reduce;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
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.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* reduce alarm and send alert data
|
||||
* common reduce alarm worker
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class AlarmCommonReduce {
|
||||
|
||||
private final AlarmGroupReduce alarmGroupReduce;
|
||||
|
||||
private ThreadPoolExecutor workerExecutor;
|
||||
|
||||
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);
|
||||
}
|
||||
public AlarmCommonReduce(AlarmGroupReduce alarmGroupReduce) {
|
||||
initWorkExecutor();
|
||||
this.alarmGroupReduce = alarmGroupReduce;
|
||||
}
|
||||
|
||||
private void initWorkExecutor() {
|
||||
ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setUncaughtExceptionHandler((thread, throwable) -> {
|
||||
log.error("alerter-reduce-worker has uncaughtException.");
|
||||
log.error(throwable.getMessage(), throwable);
|
||||
})
|
||||
.setDaemon(true)
|
||||
.setNameFormat("alerter-reduce-worker-%d")
|
||||
.build();
|
||||
workerExecutor = new ThreadPoolExecutor(2,
|
||||
2,
|
||||
10,
|
||||
TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(),
|
||||
threadFactory,
|
||||
new ThreadPoolExecutor.AbortPolicy());
|
||||
}
|
||||
|
||||
|
||||
public void reduceAndSendAlarm(SingleAlert alert) {
|
||||
workerExecutor.execute(reduceAlarmTask(alert));
|
||||
}
|
||||
|
||||
Runnable reduceAlarmTask(SingleAlert alert) {
|
||||
return () -> {
|
||||
try {
|
||||
// Generate alert fingerprint
|
||||
String fingerprint = generateAlertFingerprint(alert.getLabels());
|
||||
alert.setFingerprint(fingerprint);
|
||||
alarmGroupReduce.processGroupAlert(alert);
|
||||
} catch (Exception e) {
|
||||
log.error("Reduce alarm failed: {}", e.getMessage());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate fingerprint for alert to identify duplicates
|
||||
* Fingerprint is based on labels excluding timestamp related fields
|
||||
*/
|
||||
private String generateAlertFingerprint(Map<String, String> labels) {
|
||||
// Remove timestamp related fields
|
||||
labels.remove("timestamp");
|
||||
labels.remove("start_at");
|
||||
labels.remove("active_at");
|
||||
return labels.entrySet().stream()
|
||||
.filter(e -> !"timestamp".equals(e.getKey())
|
||||
&& !"starts_at".equals(e.getKey()) && !"actives_at".equals(e.getKey())
|
||||
&& !"end_at".equals(e.getKey()) && !"ends_at".equals(e.getKey())
|
||||
&& !"start_at".equals(e.getKey()) && !"active_at".equals(e.getKey()))
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.map(e -> e.getKey() + ":" + e.getValue())
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
}
|
||||
|
||||
-168
@@ -1,168 +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))
|
||||
|| currentAlert.getStatus() == CommonConstants.ALERT_STATUS_CODE_RESTORED) {
|
||||
// restored alert
|
||||
boolean isHasIgnore = false;
|
||||
Map<String, String> tags = currentAlert.getTags();
|
||||
if (Objects.requireNonNull(tags).containsKey(CommonConstants.IGNORE)) {
|
||||
isHasIgnore = true;
|
||||
tags.remove(CommonConstants.IGNORE);
|
||||
}
|
||||
int alertHash = generateAlertHash(CommonConstants.ALERT_PRIORITY_CODE_CRITICAL, tags);
|
||||
converageAlertMap.remove(alertHash);
|
||||
alertHash = generateAlertHash(CommonConstants.ALERT_PRIORITY_CODE_EMERGENCY, tags);
|
||||
converageAlertMap.remove(alertHash);
|
||||
alertHash = generateAlertHash(CommonConstants.ALERT_PRIORITY_CODE_WARNING, tags);
|
||||
converageAlertMap.remove(alertHash);
|
||||
if (isHasIgnore) {
|
||||
tags.put(CommonConstants.IGNORE, CommonConstants.IGNORE);
|
||||
}
|
||||
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 = generateAlertHash(currentAlert.getPriority(), currentAlert.getTags());
|
||||
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;
|
||||
}
|
||||
|
||||
private int generateAlertHash(byte priority, Map<String, String> tags) {
|
||||
List<String> keyList = tags.keySet().stream().filter(Objects::nonNull).sorted().toList();
|
||||
List<String> valueList = tags.values().stream().filter(Objects::nonNull).sorted().toList();
|
||||
return Objects.hash(priority)
|
||||
+ Arrays.hashCode(keyList.toArray(new String[0]))
|
||||
+ Arrays.hashCode(valueList.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* 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 com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dao.AlertGroupConvergeDao;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Alarm group reduce
|
||||
* refer from prometheus, code with @cursor
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class AlarmGroupReduce {
|
||||
|
||||
/**
|
||||
* Default initial group wait time 30s
|
||||
*/
|
||||
private static final long DEFAULT_GROUP_WAIT = 30 * 1000L;
|
||||
|
||||
/**
|
||||
* Default group send interval 5min
|
||||
*/
|
||||
private static final long DEFAULT_GROUP_INTERVAL = 5 * 60 * 1000L;
|
||||
|
||||
/**
|
||||
* Default repeat interval 4h
|
||||
*/
|
||||
private static final long DEFAULT_REPEAT_INTERVAL = 4 * 60 * 60 * 1000L;
|
||||
|
||||
/**
|
||||
* Milliseconds per second
|
||||
*/
|
||||
private static final long MS_PER_SECOND = 1000L;
|
||||
|
||||
/**
|
||||
* Check interval for group send ms
|
||||
*/
|
||||
private static final long CHECK_INTERVAL = 1000L;
|
||||
|
||||
private final AlarmInhibitReduce alarmInhibitReduce;
|
||||
|
||||
/**
|
||||
* Group define rules
|
||||
* key: rule name
|
||||
* value: group rule configuration
|
||||
*/
|
||||
private final Map<String, AlertGroupConverge> groupDefines;
|
||||
|
||||
/**
|
||||
* Alert cache grouped by labels
|
||||
* key: groupDefineKey:groupKey
|
||||
* value: GroupAlertCache
|
||||
*/
|
||||
private final Map<String, GroupAlertCache> groupCacheMap;
|
||||
|
||||
public AlarmGroupReduce(AlarmInhibitReduce alarmInhibitReduce, AlertGroupConvergeDao alertGroupConvergeDao) {
|
||||
this.alarmInhibitReduce = alarmInhibitReduce;
|
||||
this.groupDefines = new ConcurrentHashMap<>(8);
|
||||
this.groupCacheMap = new ConcurrentHashMap<>(8);
|
||||
List<AlertGroupConverge> groupConverges = alertGroupConvergeDao.findAlertGroupConvergesByEnableIsTrue();
|
||||
refreshGroupDefines(groupConverges);
|
||||
startCheckAndSendGroups();
|
||||
}
|
||||
|
||||
private void startCheckAndSendGroups() {
|
||||
ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setUncaughtExceptionHandler((thread, throwable) -> {
|
||||
log.error("Check alarm groups calculate has uncaughtException.");
|
||||
log.error(throwable.getMessage(), throwable);
|
||||
})
|
||||
.setDaemon(true)
|
||||
.setNameFormat("alarm-group-calculate-%d")
|
||||
.build();
|
||||
ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(threadFactory);
|
||||
scheduledExecutor.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
groupCacheMap.forEach((groupKey, cache) -> {
|
||||
if (shouldSendGroup(cache, now)) {
|
||||
sendGroupAlert(cache);
|
||||
cache.setLastSendTime(now);
|
||||
cache.getAlertFingerprints().clear();
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error("Check alarm groups calculate has exception.: {}", e.getMessage(), e);
|
||||
}
|
||||
}, 10000, CHECK_INTERVAL, java.util.concurrent.TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure group define rules
|
||||
* @param groupDefines map of group rule configurations
|
||||
*/
|
||||
public void refreshGroupDefines(List<AlertGroupConverge> groupDefines) {
|
||||
this.groupDefines.clear();
|
||||
groupDefines.forEach(define -> this.groupDefines.put(define.getName(), define));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process single alert and group by defined rules
|
||||
*/
|
||||
public void processGroupAlert(SingleAlert alert) {
|
||||
Map<String, String> labels = alert.getLabels();
|
||||
if (labels == null || labels.isEmpty() || groupDefines.isEmpty()) {
|
||||
sendSingleAlert(alert);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process each group define rule
|
||||
boolean matched = false;
|
||||
for (Map.Entry<String, AlertGroupConverge> define : groupDefines.entrySet()) {
|
||||
String defineName = define.getKey();
|
||||
AlertGroupConverge ruleConfig = define.getValue();
|
||||
|
||||
// Check if alert has all required group labels
|
||||
if (hasRequiredLabels(labels, ruleConfig.getGroupLabels())) {
|
||||
matched = true;
|
||||
processAlertByGroupDefine(alert, defineName, ruleConfig);
|
||||
}
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
sendSingleAlert(alert);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasRequiredLabels(Map<String, String> labels, List<String> requiredLabels) {
|
||||
return requiredLabels.stream().allMatch(labels::containsKey);
|
||||
}
|
||||
|
||||
private void processAlertByGroupDefine(SingleAlert alert, String defineName, AlertGroupConverge ruleConfig) {
|
||||
// Extract group labels based on define
|
||||
Map<String, String> extractedLabels = new HashMap<>();
|
||||
for (String labelKey : ruleConfig.getGroupLabels()) {
|
||||
extractedLabels.put(labelKey, alert.getLabels().get(labelKey));
|
||||
}
|
||||
|
||||
// Generate group key
|
||||
String groupKey = generateGroupKey(extractedLabels);
|
||||
|
||||
// Get or create group cache
|
||||
GroupAlertCache cache = groupCacheMap.computeIfAbsent(groupKey, k -> {
|
||||
GroupAlertCache newCache = new GroupAlertCache();
|
||||
newCache.setGroupKey(groupKey);
|
||||
newCache.setGroupLabels(extractedLabels);
|
||||
newCache.setGroupDefineName(defineName);
|
||||
newCache.setCreateTime(System.currentTimeMillis());
|
||||
newCache.setAlertFingerprints(new ConcurrentHashMap<>(8));
|
||||
return newCache;
|
||||
});
|
||||
String fingerprint = alert.getFingerprint();
|
||||
// Check if this is a duplicate alert
|
||||
SingleAlert existingAlert = cache.getAlertFingerprints().get(fingerprint);
|
||||
if (existingAlert != null) {
|
||||
// Update existing alert timestamp
|
||||
alert.setStartAt(existingAlert.getStartAt());
|
||||
cache.getAlertFingerprints().put(fingerprint, alert);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add new alert
|
||||
cache.getAlertFingerprints().put(fingerprint, alert);
|
||||
|
||||
if (shouldSendGroupImmediately(cache)) {
|
||||
sendGroupAlert(cache);
|
||||
cache.setLastSendTime(System.currentTimeMillis());
|
||||
cache.getAlertFingerprints().clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void sendGroupAlert(GroupAlertCache cache) {
|
||||
if (cache.getAlertFingerprints().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
String status = determineGroupStatus(cache.getAlertFingerprints().values());
|
||||
|
||||
// For firing alerts, check repeat interval
|
||||
if (CommonConstants.ALERT_STATUS_FIRING.equals(status)) {
|
||||
AlertGroupConverge ruleConfig = groupDefines.get(cache.getGroupDefineName());
|
||||
long repeatInterval = ruleConfig.getRepeatInterval() != null
|
||||
? ruleConfig.getRepeatInterval() * MS_PER_SECOND : DEFAULT_REPEAT_INTERVAL;
|
||||
|
||||
// Skip if within repeat interval
|
||||
if (cache.getLastRepeatTime() > 0
|
||||
&& now - cache.getLastRepeatTime() < repeatInterval) {
|
||||
return;
|
||||
}
|
||||
cache.setLastRepeatTime(now);
|
||||
}
|
||||
|
||||
GroupAlert groupAlert = GroupAlert.builder()
|
||||
.groupKey(cache.getGroupKey())
|
||||
.groupLabels(cache.getGroupLabels())
|
||||
.commonLabels(extractCommonLabels(cache.getAlertFingerprints().values()))
|
||||
.commonAnnotations(extractCommonAnnotations(cache.getAlertFingerprints().values()))
|
||||
.alerts(new ArrayList<>(cache.getAlertFingerprints().values()))
|
||||
.status(status)
|
||||
.build();
|
||||
|
||||
alarmInhibitReduce.inhibitAlarm(groupAlert);
|
||||
}
|
||||
|
||||
private boolean shouldSendGroup(GroupAlertCache cache, long now) {
|
||||
AlertGroupConverge ruleConfig = groupDefines.get(cache.getGroupDefineName());
|
||||
long groupWait = ruleConfig != null ? ruleConfig.getGroupWait() * MS_PER_SECOND : DEFAULT_GROUP_WAIT;
|
||||
long groupInterval = ruleConfig != null ? ruleConfig.getGroupInterval() * MS_PER_SECOND : DEFAULT_GROUP_INTERVAL;
|
||||
|
||||
// First wait time reached
|
||||
if (cache.getLastSendTime() == 0
|
||||
&& now - cache.getCreateTime() >= groupWait) {
|
||||
return true;
|
||||
}
|
||||
// Group interval time reached
|
||||
return cache.getLastSendTime() > 0
|
||||
&& now - cache.getLastSendTime() >= groupInterval;
|
||||
}
|
||||
|
||||
private boolean shouldSendGroupImmediately(GroupAlertCache cache) {
|
||||
// Check if all alerts are resolved
|
||||
return cache.getAlertFingerprints().values().stream()
|
||||
.allMatch(alert -> CommonConstants.ALERT_STATUS_RESOLVED.equals(alert.getStatus()));
|
||||
}
|
||||
|
||||
private void sendSingleAlert(SingleAlert alert) {
|
||||
// Wrap single alert as group alert
|
||||
String groupKey = generateGroupKey(alert.getLabels());
|
||||
GroupAlert groupAlert = GroupAlert.builder()
|
||||
.groupKey(groupKey)
|
||||
.groupLabels(alert.getLabels())
|
||||
.commonLabels(alert.getLabels())
|
||||
.commonAnnotations(alert.getAnnotations())
|
||||
.alerts(new LinkedList<>(List.of(alert)))
|
||||
.status(alert.getStatus())
|
||||
.build();
|
||||
|
||||
alarmInhibitReduce.inhibitAlarm(groupAlert);
|
||||
}
|
||||
|
||||
private String generateGroupKey(Map<String, String> labels) {
|
||||
return labels.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.map(e -> e.getKey() + ":" + e.getValue())
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
private Map<String, String> extractCommonLabels(Collection<SingleAlert> alerts) {
|
||||
// Extract common labels from all alerts
|
||||
if (alerts.isEmpty()) {
|
||||
return new HashMap<>(8);
|
||||
}
|
||||
Map<String, String> common = new HashMap<>(alerts.stream().findFirst().get().getLabels());
|
||||
alerts.forEach(alert -> {
|
||||
common.keySet().removeIf(key ->
|
||||
!alert.getLabels().containsKey(key)
|
||||
|| !common.get(key).equals(alert.getLabels().get(key)));
|
||||
});
|
||||
return common;
|
||||
}
|
||||
|
||||
private Map<String, String> extractCommonAnnotations(Collection<SingleAlert> alerts) {
|
||||
// Extract common annotations from all alerts
|
||||
if (alerts.isEmpty()) {
|
||||
return new HashMap<>(8);
|
||||
}
|
||||
Map<String, String> common = new HashMap<>(alerts.stream().findFirst().get().getAnnotations());
|
||||
alerts.forEach(alert -> {
|
||||
common.keySet().removeIf(key ->
|
||||
!alert.getAnnotations().containsKey(key)
|
||||
|| !common.get(key).equals(alert.getAnnotations().get(key)));
|
||||
});
|
||||
return common;
|
||||
}
|
||||
|
||||
private String determineGroupStatus(Collection<SingleAlert> alerts) {
|
||||
// If any alert is firing, group is firing
|
||||
return alerts.stream()
|
||||
.anyMatch(alert -> CommonConstants.ALERT_STATUS_FIRING.equals(alert.getStatus()))
|
||||
? CommonConstants.ALERT_STATUS_FIRING : CommonConstants.ALERT_STATUS_RESOLVED;
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class GroupAlertCache {
|
||||
private String groupDefineName;
|
||||
private String groupKey;
|
||||
private Map<String, String> groupLabels;
|
||||
private Map<String, SingleAlert> alertFingerprints = new ConcurrentHashMap<>(8);
|
||||
private long createTime;
|
||||
private long lastSendTime;
|
||||
private long lastRepeatTime;
|
||||
}
|
||||
}
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* 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 com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.AlerterProperties;
|
||||
import org.apache.hertzbeat.alert.dao.AlertInhibitDao;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertInhibit;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* inhibit alarm
|
||||
* refer from prometheus, code with @cursor
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class AlarmInhibitReduce {
|
||||
|
||||
/**
|
||||
* Interval for checking and cleaning up expired source alerts
|
||||
*/
|
||||
private static final long CHECK_INTERVAL = 60_000L;
|
||||
|
||||
/**
|
||||
* alarm silence
|
||||
*/
|
||||
private final AlarmSilenceReduce alarmSilenceReduce;
|
||||
|
||||
/**
|
||||
* rule cache
|
||||
*/
|
||||
private final Map<Long, AlertInhibit> inhibitRules;
|
||||
|
||||
/**
|
||||
* Cache for source alerts
|
||||
* key: ruleId
|
||||
* value: map of source alerts with their fingerprints
|
||||
*/
|
||||
private final Map<Long, Map<String, SourceAlertEntry>> sourceAlertCache;
|
||||
|
||||
/**
|
||||
* Default TTL for source alerts (4 hours)
|
||||
*/
|
||||
private static long SOURCE_ALERT_TTL = 4 * 60 * 60 * 1000L;
|
||||
|
||||
public AlarmInhibitReduce(AlarmSilenceReduce alarmSilenceReduce, AlertInhibitDao alertInhibitDao
|
||||
, AlerterProperties alerterProperties) {
|
||||
this.alarmSilenceReduce = alarmSilenceReduce;
|
||||
if (alerterProperties.getInhibit() != null && alerterProperties.getInhibit().getTtl() > 0) {
|
||||
SOURCE_ALERT_TTL = alerterProperties.getInhibit().getTtl();
|
||||
}
|
||||
inhibitRules = new ConcurrentHashMap<>(8);
|
||||
sourceAlertCache = new ConcurrentHashMap<>(8);
|
||||
List<AlertInhibit> inhibits = alertInhibitDao.findAlertInhibitsByEnableIsTrue();
|
||||
refreshInhibitRules(inhibits);
|
||||
startScheduledCleanupCache();
|
||||
}
|
||||
|
||||
private void startScheduledCleanupCache() {
|
||||
ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setUncaughtExceptionHandler((thread, throwable) -> {
|
||||
log.error("Scheduled clean up inhibit cache has uncaughtException.");
|
||||
log.error(throwable.getMessage(), throwable);
|
||||
})
|
||||
.setDaemon(true)
|
||||
.setNameFormat("inhibit-clean-up-%d")
|
||||
.build();
|
||||
ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(threadFactory);
|
||||
// Scheduled cleanup of all expired source alerts
|
||||
scheduledExecutor.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
sourceAlertCache.values().forEach(this::cleanupExpiredEntries);
|
||||
// Remove empty rule caches
|
||||
sourceAlertCache.entrySet().removeIf(entry -> entry.getValue().isEmpty());
|
||||
} catch (Exception e) {
|
||||
log.error("Error during scheduled cleanup", e);
|
||||
}
|
||||
}, CHECK_INTERVAL, CHECK_INTERVAL, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure inhibit rules
|
||||
* @param rules Inhibit rule list
|
||||
*/
|
||||
public void refreshInhibitRules(List<AlertInhibit> rules) {
|
||||
if (rules == null) {
|
||||
log.warn("Attempted to refresh inhibit rules with null list.");
|
||||
return;
|
||||
}
|
||||
this.inhibitRules.clear();
|
||||
rules.forEach(rule -> this.inhibitRules.put(rule.getId(), rule));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process alert with inhibit rules
|
||||
* If alert is inhibited, it will not be forwarded
|
||||
* @param groupAlert Grouped and pending alerts to be processed
|
||||
*/
|
||||
public void inhibitAlarm(GroupAlert groupAlert) {
|
||||
if (groupAlert == null) {
|
||||
log.warn("Received null GroupAlert. Skipping processing.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (inhibitRules.isEmpty()) {
|
||||
alarmSilenceReduce.silenceAlarm(groupAlert);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process each individual alert
|
||||
for (var alert : groupAlert.getAlerts()) {
|
||||
for (AlertInhibit rule : inhibitRules.values()) {
|
||||
if (isSourceAlert(alert, rule)) {
|
||||
cacheSourceAlert(alert, rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out inhibited alerts
|
||||
groupAlert.getAlerts().removeIf(this::shouldInhibit);
|
||||
|
||||
// Continue processing if there are remaining alerts
|
||||
if (!groupAlert.getAlerts().isEmpty()) {
|
||||
alarmSilenceReduce.silenceAlarm(groupAlert);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error inhibiting alarm for {}", groupAlert, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if alert matches inhibit rule source labels
|
||||
* @param alert Single alert to be processed
|
||||
* @param rule The rule of inhibition
|
||||
*/
|
||||
private boolean isSourceAlert(SingleAlert alert, AlertInhibit rule) {
|
||||
if (alert == null || rule == null) {
|
||||
log.warn("Received null alert or rule in isSourceAlert");
|
||||
return false;
|
||||
}
|
||||
if (!CommonConstants.ALERT_STATUS_FIRING.equals(alert.getStatus())) {
|
||||
return false;
|
||||
}
|
||||
return matchLabels(alert.getLabels(), rule.getSourceLabels());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if alert should be inhibited by any active source alerts
|
||||
* @param alert Single alert to be processed
|
||||
*/
|
||||
private boolean shouldInhibit(SingleAlert alert) {
|
||||
if (alert == null) {
|
||||
log.warn("Received null alert in shouldInhibit");
|
||||
return false;
|
||||
}
|
||||
if (CommonConstants.ALERT_STATUS_RESOLVED.equals(alert.getStatus())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AlertInhibit rule : inhibitRules.values()) {
|
||||
if (!matchLabels(alert.getLabels(), rule.getTargetLabels())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<SingleAlert> sourceAlerts = getActiveSourceAlerts(rule);
|
||||
if (sourceAlerts.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (SingleAlert source : sourceAlerts) {
|
||||
if (matchEqualLabels(source, alert, rule.getEqualLabels())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all required labels match
|
||||
* @param alertLabels The label of the alarm
|
||||
* @param requiredLabels Labels to be matched
|
||||
*/
|
||||
private boolean matchLabels(Map<String, String> alertLabels, Map<String, String> requiredLabels) {
|
||||
if (alertLabels == null || requiredLabels == null) {
|
||||
log.warn("Received null alertLabels or requiredLabels in matchLabels");
|
||||
return false;
|
||||
}
|
||||
return requiredLabels.entrySet().stream()
|
||||
.allMatch(entry -> entry.getValue().equals(alertLabels.get(entry.getKey())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if equal labels have same values in both alerts
|
||||
* @param source Alert used to suppress other alerts
|
||||
* @param target Alert that may be suppressed
|
||||
* @param equalLabels Need to be equal labels
|
||||
*/
|
||||
private boolean matchEqualLabels(SingleAlert source, SingleAlert target, List<String> equalLabels) {
|
||||
if (source == null || target == null) {
|
||||
log.warn("Received null source or target in matchEqualLabels");
|
||||
return false;
|
||||
}
|
||||
if (equalLabels == null || equalLabels.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
Map<String, String> sourceLabels = source.getLabels();
|
||||
Map<String, String> targetLabels = target.getLabels();
|
||||
|
||||
return equalLabels.stream().allMatch(label -> {
|
||||
String sourceValue = sourceLabels.get(label);
|
||||
String targetValue = targetLabels.get(label);
|
||||
return sourceValue != null && sourceValue.equals(targetValue);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache source alert for inhibit rule
|
||||
* @param alert Single alert to be processed
|
||||
* @param rule The rule of inhibition
|
||||
*/
|
||||
private void cacheSourceAlert(SingleAlert alert, AlertInhibit rule) {
|
||||
if (alert == null || rule == null) {
|
||||
log.warn("Received null alert or rule in cacheSourceAlert");
|
||||
return;
|
||||
}
|
||||
Map<String, SourceAlertEntry> ruleCache = sourceAlertCache.computeIfAbsent(
|
||||
rule.getId(),
|
||||
k -> new ConcurrentHashMap<>()
|
||||
);
|
||||
|
||||
SourceAlertEntry entry = new SourceAlertEntry(
|
||||
alert,
|
||||
System.currentTimeMillis(),
|
||||
System.currentTimeMillis() + SOURCE_ALERT_TTL
|
||||
);
|
||||
ruleCache.put(alert.getFingerprint(), entry);
|
||||
cleanupExpiredEntries(ruleCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active source alerts for inhibit rule
|
||||
* @param rule The rule of inhibition
|
||||
* @return List of active source alerts
|
||||
*/
|
||||
private List<SingleAlert> getActiveSourceAlerts(AlertInhibit rule) {
|
||||
if (rule == null) {
|
||||
log.warn("Received null rule in getActiveSourceAlerts");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Map<String, SourceAlertEntry> ruleCache = sourceAlertCache.get(rule.getId());
|
||||
if (ruleCache == null || ruleCache.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
return ruleCache.values().stream()
|
||||
.filter(entry -> entry.getExpiryTime() > now)
|
||||
.map(SourceAlertEntry::getAlert)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove expired entries from cache
|
||||
* @param cache Source alert cache entry map
|
||||
*/
|
||||
private void cleanupExpiredEntries(Map<String, SourceAlertEntry> cache) {
|
||||
if (cache == null) {
|
||||
log.warn("Received null cache in cleanupExpiredEntries");
|
||||
return;
|
||||
}
|
||||
long now = System.currentTimeMillis();
|
||||
cache.entrySet().removeIf(entry -> entry.getValue().getExpiryTime() <= now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Source alert cache entry
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
private static class SourceAlertEntry {
|
||||
private final SingleAlert alert;
|
||||
private final long createTime;
|
||||
private final long expiryTime;
|
||||
}
|
||||
}
|
||||
+48
-62
@@ -23,103 +23,89 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.hertzbeat.alert.dao.AlertSilenceDao;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeDispatch;
|
||||
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;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* silence alarm
|
||||
* Silence alert handler
|
||||
* Refer from prometheus alert silencing mechanism
|
||||
*/
|
||||
@Service
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class AlarmSilenceReduce {
|
||||
|
||||
private final AlertSilenceDao alertSilenceDao;
|
||||
private final AlertNoticeDispatch dispatcherAlarm;
|
||||
|
||||
/**
|
||||
* alert silence filter data
|
||||
* @param alert alert
|
||||
* @return true when not filter
|
||||
* Process alert with silence rules
|
||||
* If alert matches any active silence rule, it will be silenced
|
||||
* @param groupAlert The alert to be processed
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean filterSilence(Alert alert) {
|
||||
CommonCacheService<String, Object> silenceCache = CacheFactory.getAlertSilenceCache();
|
||||
List<AlertSilence> alertSilenceList = (List<AlertSilence>) silenceCache.get(CommonConstants.CACHE_ALERT_SILENCE);
|
||||
public void silenceAlarm(GroupAlert groupAlert) {
|
||||
List<AlertSilence> alertSilenceList = CacheFactory.getAlertSilenceCache();
|
||||
if (alertSilenceList == null) {
|
||||
alertSilenceList = alertSilenceDao.findAll();
|
||||
silenceCache.put(CommonConstants.CACHE_ALERT_SILENCE, alertSilenceList);
|
||||
alertSilenceList = alertSilenceDao.findAlertSilencesByEnableTrue();
|
||||
CacheFactory.setAlertSilenceCache(alertSilenceList);
|
||||
}
|
||||
|
||||
// Check each silence rule
|
||||
for (AlertSilence alertSilence : alertSilenceList) {
|
||||
if (!alertSilence.isEnable()) {
|
||||
continue;
|
||||
}
|
||||
// if match the silence rule, return
|
||||
// Check if alert matches silence rule
|
||||
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 && groupAlert.getGroupLabels() != null) {
|
||||
Map<String, String> labels = alertSilence.getLabels();
|
||||
Map<String, String> alertLabels = groupAlert.getGroupLabels();
|
||||
match = labels.entrySet().stream().anyMatch(item ->
|
||||
alertLabels.containsKey(item.getKey()) && item.getValue().equals(alertLabels.get(item.getKey())));
|
||||
}
|
||||
|
||||
if (match) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (alertSilence.getType() == 0) {
|
||||
// once time
|
||||
return checkAndSave(LocalDateTime.now(), alertSilence);
|
||||
// One-time silence rule
|
||||
if (checkAndSave(now, alertSilence)) {
|
||||
continue;
|
||||
}
|
||||
// Alert is silenced
|
||||
return;
|
||||
} else if (alertSilence.getType() == 1) {
|
||||
// cyc time
|
||||
int currentDayOfWeek = LocalDateTime.now().toLocalDate().getDayOfWeek().getValue();
|
||||
if (alertSilence.getDays() != null && !alertSilence.getDays().isEmpty()) {
|
||||
boolean dayMatch = alertSilence.getDays().stream().anyMatch(item -> item == currentDayOfWeek);
|
||||
if (dayMatch) {
|
||||
return checkAndSave(LocalDateTime.now(), alertSilence);
|
||||
}
|
||||
// Cyclic silence rule
|
||||
int currentDayOfWeek = now.getDayOfWeek().getValue();
|
||||
if (alertSilence.getDays() != null && alertSilence.getDays().contains((byte) currentDayOfWeek)
|
||||
&& !checkAndSave(now, alertSilence)) {
|
||||
// Alert is silenced
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
// No matching silence rule, forward the alert
|
||||
dispatcherAlarm.dispatchAlarm(groupAlert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check AlertSilence start and end match, to save alertSilence obj.
|
||||
* @param times LocalDateTime.
|
||||
* @param alertSilence {@link AlertSilence}
|
||||
* @return boolean
|
||||
* Check if alert time is within silence period and update silence rule counter
|
||||
* @param now Current time
|
||||
* @param alertSilence Silence rule to check
|
||||
* @return true if alert should not be silenced, false if alert should be silenced
|
||||
*/
|
||||
private boolean checkAndSave(LocalDateTime times, AlertSilence alertSilence) {
|
||||
|
||||
boolean startMatch = alertSilence.getPeriodStart() == null || times.isAfter(alertSilence.getPeriodStart().toLocalDateTime());
|
||||
boolean endMatch = alertSilence.getPeriodEnd() == null || times.isBefore(alertSilence.getPeriodEnd().toLocalDateTime());
|
||||
private boolean checkAndSave(LocalDateTime now, AlertSilence alertSilence) {
|
||||
boolean startMatch = alertSilence.getPeriodStart() == null
|
||||
|| now.isAfter(alertSilence.getPeriodStart().toLocalDateTime());
|
||||
boolean endMatch = alertSilence.getPeriodEnd() == null
|
||||
|| now.isBefore(alertSilence.getPeriodEnd().toLocalDateTime());
|
||||
|
||||
if (startMatch && endMatch) {
|
||||
|
||||
int time = Optional.ofNullable(alertSilence.getTimes()).orElse(0);
|
||||
alertSilence.setTimes(time + 1);
|
||||
alertSilenceDao.save(alertSilence);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-47
@@ -19,13 +19,9 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -77,59 +73,18 @@ public interface AlertDefineService {
|
||||
* @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 defineIds Alarm Definition ID List
|
||||
* @param search Search-Target Expr Template
|
||||
* @param priority Alarm Definition Severity
|
||||
* @param sort Sort field
|
||||
* @param order Sort mode: asc: ascending, desc: descending
|
||||
* @param pageIndex List current page
|
||||
* @param pageSize Number of list pages
|
||||
* @return The query results
|
||||
*/
|
||||
Page<AlertDefine> getAlertDefines(List<Long> defineIds, String search, Byte priority, String sort, String order, int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
Page<AlertDefine> getAlertDefines(List<Long> defineIds, String search, String sort, String order, int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* Export file configuration of specified type based on ID list and export file type
|
||||
@@ -146,4 +101,11 @@ public interface AlertDefineService {
|
||||
* @throws Exception An exception was thrown during the importConfig
|
||||
*/
|
||||
void importConfig(MultipartFile file) throws Exception;
|
||||
|
||||
/**
|
||||
* Get the real-time alarm definition list
|
||||
* @return Real-time alarm definition list
|
||||
*/
|
||||
List<AlertDefine> getRealTimeAlertDefines();
|
||||
|
||||
}
|
||||
|
||||
+18
-18
@@ -19,50 +19,50 @@ package org.apache.hertzbeat.alert.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertConverge;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
/**
|
||||
* management interface service for alert converge
|
||||
*/
|
||||
public interface AlertConvergeService {
|
||||
public interface AlertGroupConvergeService {
|
||||
/**
|
||||
* Verify the correctness of the request data parameters
|
||||
* @param alertConverge AlertConverge
|
||||
* @param alertGroupConverge AlertGroupConverge
|
||||
* @param isModify whether modify
|
||||
* @throws IllegalArgumentException A checksum parameter error is thrown
|
||||
*/
|
||||
void validate(AlertConverge alertConverge, boolean isModify) throws IllegalArgumentException;
|
||||
void validate(AlertGroupConverge alertGroupConverge, boolean isModify) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* New AlertConverge
|
||||
* @param alertConverge AlertConverge Entity
|
||||
* New AlertGroupConverge
|
||||
* @param alertGroupConverge AlertGroupConverge Entity
|
||||
* @throws RuntimeException Added procedure exception throwing
|
||||
*/
|
||||
void addAlertConverge(AlertConverge alertConverge) throws RuntimeException;
|
||||
void addAlertGroupConverge(AlertGroupConverge alertGroupConverge) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Modifying an AlertConverge
|
||||
* @param alertConverge Alarm definition Entity
|
||||
* Modifying an AlertGroupConverge
|
||||
* @param alertGroupConverge Alarm definition Entity
|
||||
* @throws RuntimeException Exception thrown during modification
|
||||
*/
|
||||
void modifyAlertConverge(AlertConverge alertConverge) throws RuntimeException;
|
||||
void modifyAlertGroupConverge(AlertGroupConverge alertGroupConverge) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Obtain AlertConverge information
|
||||
* @param convergeId AlertConverge ID
|
||||
* @return AlertConverge
|
||||
* Obtain AlertGroupConverge information
|
||||
* @param convergeId AlertGroupConverge ID
|
||||
* @return AlertGroupConverge
|
||||
* @throws RuntimeException An exception was thrown during the query
|
||||
*/
|
||||
AlertConverge getAlertConverge(long convergeId) throws RuntimeException;
|
||||
AlertGroupConverge getAlertGroupConverge(long convergeId) throws RuntimeException;
|
||||
|
||||
|
||||
/**
|
||||
* Delete AlertConverge in batches
|
||||
* @param convergeIds AlertConverge IDs
|
||||
* Delete AlertGroupConverge in batches
|
||||
* @param convergeIds AlertGroupConverge IDs
|
||||
* @throws RuntimeException Exception thrown during deletion
|
||||
*/
|
||||
void deleteAlertConverges(Set<Long> convergeIds) throws RuntimeException;
|
||||
void deleteAlertGroupConverges(Set<Long> convergeIds) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
@@ -74,5 +74,5 @@ public interface AlertConvergeService {
|
||||
* @param pageSize Number of list pages
|
||||
* @return The query results
|
||||
*/
|
||||
Page<AlertConverge> getAlertConverges(List<Long> convergeIds, String search, String sort, String order, int pageIndex, int pageSize);
|
||||
Page<AlertGroupConverge> getAlertGroupConverges(List<Long> convergeIds, String search, String sort, String order, int pageIndex, int pageSize);
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.List;
|
||||
import java.util.Set;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertInhibit;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
/**
|
||||
* management interface service for alert inhibit
|
||||
*/
|
||||
public interface AlertInhibitService {
|
||||
/**
|
||||
* Verify the correctness of the request data parameters
|
||||
* @param alertInhibit AlertInhibit
|
||||
* @param isModify whether modify
|
||||
* @throws IllegalArgumentException A checksum parameter error is thrown
|
||||
*/
|
||||
void validate(AlertInhibit alertInhibit, boolean isModify) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* New AlertInhibit
|
||||
* @param alertInhibit AlertInhibit Entity
|
||||
* @throws RuntimeException Added procedure exception throwing
|
||||
*/
|
||||
void addAlertInhibit(AlertInhibit alertInhibit) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Modifying an AlertInhibit
|
||||
* @param alertInhibit Alarm definition Entity
|
||||
* @throws RuntimeException Exception thrown during modification
|
||||
*/
|
||||
void modifyAlertInhibit(AlertInhibit alertInhibit) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Obtain AlertInhibit information
|
||||
* @param inhibitId AlertInhibit ID
|
||||
* @return AlertInhibit
|
||||
* @throws RuntimeException An exception was thrown during the query
|
||||
*/
|
||||
AlertInhibit getAlertInhibit(long inhibitId) throws RuntimeException;
|
||||
|
||||
|
||||
/**
|
||||
* Delete AlertInhibit in batches
|
||||
* @param inhibitIds AlertInhibit IDs
|
||||
* @throws RuntimeException Exception thrown during deletion
|
||||
*/
|
||||
void deleteAlertInhibits(Set<Long> inhibitIds) throws RuntimeException;
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param inhibitIds Alarm Silence ID
|
||||
* @param search Search Name
|
||||
* @param sort Sort field
|
||||
* @param order Sort mode: asc: ascending, desc: descending
|
||||
* @param pageIndex List current page
|
||||
* @param pageSize Number of list pages
|
||||
* @return The query results
|
||||
*/
|
||||
Page<AlertInhibit> getAlertInhibits(List<Long> inhibitIds, String search, String sort, String order, int pageIndex, int pageSize);
|
||||
}
|
||||
+29
-41
@@ -20,10 +20,9 @@ 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.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
* Alarm information management interface
|
||||
@@ -31,68 +30,57 @@ import org.springframework.data.jpa.domain.Specification;
|
||||
public interface AlertService {
|
||||
|
||||
/**
|
||||
* Add alarm record
|
||||
* @param alert Alert entity
|
||||
* @throws RuntimeException Add process exception throw
|
||||
* get and query single alerts
|
||||
* @param status status
|
||||
* @param search search
|
||||
* @param sort sort
|
||||
* @param order order
|
||||
* @param pageIndex pageIndex
|
||||
* @param pageSize pageSize
|
||||
* @return single alerts
|
||||
*/
|
||||
void addAlert(Alert alert) throws RuntimeException;
|
||||
|
||||
Page<SingleAlert> getSingleAlerts(String status, String search, String sort, String order, int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param alarmIds Alarm ID List
|
||||
* @param monitorId Monitor ID
|
||||
* @param priority Alarm level
|
||||
* @param status Alarm Status
|
||||
* @param content Alarm content fuzzy query
|
||||
* @param sort Sort field
|
||||
* @param order Sort Type
|
||||
* @param pageIndex List current page
|
||||
* @param pageSize Number of list pagination
|
||||
* @return search result
|
||||
*/
|
||||
Page<Alert> getAlerts(List<Long> alarmIds, Long monitorId, Byte priority, Byte status, String content, String sort, String order, int pageIndex, int pageSize);
|
||||
Page<GroupAlert> getGroupAlerts(String status, String search, String sort, String order, int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* Delete alarms in batches according to the alarm ID list
|
||||
* @param ids Alarm ID List
|
||||
* delete the group alarm according to the alarm ID
|
||||
* @param ids Alarm ID List
|
||||
*/
|
||||
void deleteAlerts(HashSet<Long> ids);
|
||||
void deleteGroupAlerts(HashSet<Long> ids);
|
||||
|
||||
/**
|
||||
* Clear all alerts
|
||||
* delete the single alarm according to the alarm ID
|
||||
* @param ids Alarm ID List
|
||||
*/
|
||||
void clearAlerts();
|
||||
void deleteSingleAlerts(HashSet<Long> ids);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param status Alarm status to be modified
|
||||
* @param ids Alarm ID List to be modified
|
||||
*/
|
||||
void editAlertStatus(Byte status, List<Long> ids);
|
||||
void editGroupAlertStatus(String status, List<Long> ids);
|
||||
|
||||
/**
|
||||
* 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 editSingleAlertStatus(String 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);
|
||||
|
||||
/**
|
||||
* Save external alarms of cloud services
|
||||
* @param cloudServiceName cloud service name,Such as tencloud
|
||||
* @param alertReport alert report json string
|
||||
*/
|
||||
void addNewAlertReportFromCloud(String cloudServiceName, String alertReport);
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param specification Query conditions
|
||||
* @return search result
|
||||
*/
|
||||
List<Alert> getAlerts(Specification<Alert> specification);
|
||||
}
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* datasource service
|
||||
*/
|
||||
public interface DataSourceService {
|
||||
|
||||
/**
|
||||
* execute query expr calculate
|
||||
* @param datasource datasource
|
||||
* @param expr query expr
|
||||
* @return result
|
||||
*/
|
||||
List<Map<String, Object>> calculate(String datasource, String expr);
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Extern alert service for prometheus, cloud and so on
|
||||
*/
|
||||
public interface ExternAlertService {
|
||||
|
||||
/**
|
||||
* Save external alarms of services
|
||||
* @param content alert report json string
|
||||
*/
|
||||
void addExternAlert(String content);
|
||||
|
||||
|
||||
/**
|
||||
* Support source: prometheus, tencent ...
|
||||
* @return source name
|
||||
*/
|
||||
String supportSource();
|
||||
}
|
||||
+31
-10
@@ -15,14 +15,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.service;
|
||||
package org.apache.hertzbeat.alert.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.apache.hertzbeat.common.entity.alerter.Alert;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
/**
|
||||
* Message notification configuration interface
|
||||
@@ -32,23 +33,32 @@ public interface NoticeConfigService {
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param name Recipient name,support fuzzy query
|
||||
* @param pageIndex Page number
|
||||
* @param pageSize Number of records per page
|
||||
* @return Search result
|
||||
*/
|
||||
List<NoticeReceiver> getNoticeReceivers(String name);
|
||||
Page<NoticeReceiver> getNoticeReceivers(String name, int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param name Template name,support fuzzy query
|
||||
* @param preset Whether it is a system preset template
|
||||
* true: System preset template
|
||||
* false: Custom template
|
||||
* @param pageIndex Page number
|
||||
* @param pageSize Number of records per page
|
||||
* @return Search result
|
||||
*/
|
||||
List<NoticeTemplate> getNoticeTemplates(String name);
|
||||
Page<NoticeTemplate> getNoticeTemplates(String name, boolean preset, int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* Dynamic conditional query
|
||||
* @param name Recipient name
|
||||
* @param name Recipient name ,support fuzzy query
|
||||
* @param pageIndex Page number
|
||||
* @param pageSize Number of records per page
|
||||
* @return Search result
|
||||
*/
|
||||
List<NoticeRule> getNoticeRules(String name);
|
||||
Page<NoticeRule> getNoticeRules(String name, int pageIndex, int pageSize);
|
||||
|
||||
/**
|
||||
* Add a notification recipient
|
||||
@@ -91,7 +101,7 @@ public interface NoticeConfigService {
|
||||
* @param alert Alarm information
|
||||
* @return Receiver
|
||||
*/
|
||||
List<NoticeRule> getReceiverFilterRule(Alert alert);
|
||||
List<NoticeRule> getReceiverFilterRule(GroupAlert alert);
|
||||
|
||||
/**
|
||||
* Query the template information according to the template ID
|
||||
@@ -154,4 +164,15 @@ public interface NoticeConfigService {
|
||||
*/
|
||||
boolean sendTestMsg(NoticeReceiver noticeReceiver);
|
||||
|
||||
/**
|
||||
* Query all notification recipients
|
||||
* @return Recipient List
|
||||
*/
|
||||
List<NoticeReceiver> getAllNoticeReceivers();
|
||||
|
||||
/**
|
||||
* Query all notification policies
|
||||
* @return Notification Policy List
|
||||
*/
|
||||
List<NoticeTemplate> getAllNoticeTemplates();
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.service;
|
||||
package org.apache.hertzbeat.alert.service;
|
||||
|
||||
import com.tencentcloudapi.common.Credential;
|
||||
import com.tencentcloudapi.sms.v20210111.SmsClient;
|
||||
+58
-59
@@ -25,17 +25,17 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dto.AlertDefineDTO;
|
||||
import org.apache.hertzbeat.alert.dto.ExportAlertDefineDTO;
|
||||
import org.apache.hertzbeat.common.entity.manager.TagItem;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.hertzbeat.common.util.export.ExcelExportUtils;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
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;
|
||||
@@ -97,15 +97,6 @@ public class AlertDefineExcelImExportServiceImpl extends AlertDefineAbstractImEx
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -153,18 +144,17 @@ public class AlertDefineExcelImExportServiceImpl extends AlertDefineAbstractImEx
|
||||
|
||||
|
||||
private AlertDefineDTO extractAlertDefineDataFromRow(Row row) {
|
||||
TypeReference<Map<String, String>> typeReference = new TypeReference<>() {};
|
||||
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.setName(getCellValueAsString(row.getCell(0)));
|
||||
alertDefineDTO.setType(getCellValueAsString(row.getCell(1)));
|
||||
alertDefineDTO.setExpr(getCellValueAsString(row.getCell(2)));
|
||||
alertDefineDTO.setPeriod(getCellValueAsInteger(row.getCell(3)));
|
||||
alertDefineDTO.setTimes(getCellValueAsInteger(row.getCell(4)));
|
||||
alertDefineDTO.setLabels(JsonUtil.fromJson(getCellValueAsString(row.getCell(5)), typeReference));
|
||||
alertDefineDTO.setAnnotations(JsonUtil.fromJson(getCellValueAsString(row.getCell(6)), typeReference));
|
||||
alertDefineDTO.setTemplate(getCellValueAsString(row.getCell(7)));
|
||||
alertDefineDTO.setEnable(getCellValueAsBoolean(row.getCell(8)));
|
||||
alertDefineDTO.setRecoverNotice(getCellValueAsBoolean(row.getCell(9)));
|
||||
alertDefineDTO.setTemplate(getCellValueAsString(row.getCell(10)));
|
||||
return alertDefineDTO;
|
||||
}
|
||||
|
||||
@@ -178,11 +168,31 @@ public class AlertDefineExcelImExportServiceImpl extends AlertDefineAbstractImEx
|
||||
public void writeOs(List<ExportAlertDefineDTO> exportAlertDefineList, OutputStream os) {
|
||||
try {
|
||||
|
||||
Workbook workbook = new HSSFWorkbook();
|
||||
Workbook workbook = WorkbookFactory.create(true);
|
||||
String sheetName = "Export AlertDefine";
|
||||
Sheet sheet = ExcelExportUtils.setSheet(sheetName, workbook, AlertDefineDTO.class);
|
||||
Sheet sheet = workbook.createSheet(sheetName);
|
||||
sheet.setDefaultColumnWidth(20);
|
||||
sheet.setColumnWidth(2, 40 * 256);
|
||||
sheet.setColumnWidth(5, 40 * 256);
|
||||
sheet.setColumnWidth(6, 40 * 256);
|
||||
sheet.setColumnWidth(7, 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 = ExcelExportUtils.setCellStyle(workbook);
|
||||
CellStyle cellStyle = workbook.createCellStyle();
|
||||
cellStyle.setAlignment(HorizontalAlignment.CENTER);
|
||||
// set header
|
||||
String[] headers = {"Name", "Type", "Expr", "Period", "Times", "Labels", "Annotations", "Template", "Enable"};
|
||||
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;
|
||||
@@ -190,44 +200,33 @@ public class AlertDefineExcelImExportServiceImpl extends AlertDefineAbstractImEx
|
||||
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);
|
||||
Cell nameCell = row.createCell(0);
|
||||
nameCell.setCellValue(alertDefineDTO.getName());
|
||||
nameCell.setCellStyle(cellStyle);
|
||||
Cell typeCell = row.createCell(1);
|
||||
typeCell.setCellValue(alertDefineDTO.getType());
|
||||
typeCell.setCellStyle(cellStyle);
|
||||
Cell exprCell = row.createCell(2);
|
||||
exprCell.setCellValue(alertDefineDTO.getExpr());
|
||||
exprCell.setCellStyle(cellStyle);
|
||||
Cell priorityCell = row.createCell(5);
|
||||
priorityCell.setCellValue(alertDefineDTO.getPriority());
|
||||
priorityCell.setCellStyle(cellStyle);
|
||||
Cell timesCell = row.createCell(6);
|
||||
Cell periodCell = row.createCell(3);
|
||||
periodCell.setCellValue(alertDefineDTO.getPeriod());
|
||||
periodCell.setCellStyle(cellStyle);
|
||||
Cell timesCell = row.createCell(4);
|
||||
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);
|
||||
timesCell.setCellStyle(cellStyle);
|
||||
Cell labelsCell = row.createCell(5);
|
||||
labelsCell.setCellValue(JsonUtil.toJson(alertDefineDTO.getLabels()));
|
||||
labelsCell.setCellStyle(cellStyle);
|
||||
Cell annoCell = row.createCell(6);
|
||||
annoCell.setCellValue(JsonUtil.toJson(alertDefineDTO.getAnnotations()));
|
||||
annoCell.setCellStyle(cellStyle);
|
||||
Cell templateCell = row.createCell(7);
|
||||
templateCell.setCellValue(alertDefineDTO.getTemplate());
|
||||
recoverNoticeCell.setCellStyle(cellStyle);
|
||||
templateCell.setCellStyle(cellStyle);
|
||||
Cell enableCell = row.createCell(8);
|
||||
enableCell.setCellValue(alertDefineDTO.getEnable());
|
||||
enableCell.setCellStyle(cellStyle);
|
||||
}
|
||||
workbook.write(os);
|
||||
os.close();
|
||||
|
||||
+42
-84
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import static org.apache.hertzbeat.common.constants.CommonConstants.ALERT_THRESHOLD_TYPE_REALTIME;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -24,16 +25,14 @@ import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDefineBindDao;
|
||||
import org.apache.hertzbeat.alert.calculate.PeriodicAlertRuleScheduler;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDefineDao;
|
||||
import org.apache.hertzbeat.alert.dao.AlertMonitorDao;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineImExportService;
|
||||
import org.apache.hertzbeat.alert.service.AlertDefineService;
|
||||
import org.apache.hertzbeat.common.cache.CacheFactory;
|
||||
import org.apache.hertzbeat.common.constants.ExportFileConstants;
|
||||
import org.apache.hertzbeat.common.constants.SignConstants;
|
||||
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.FileUtil;
|
||||
import org.apache.hertzbeat.common.util.JexlExpressionRunner;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -53,14 +52,11 @@ import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
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.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Alarm definition management interface implementation
|
||||
@@ -72,12 +68,9 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
|
||||
@Autowired
|
||||
private AlertDefineDao alertDefineDao;
|
||||
|
||||
|
||||
@Autowired
|
||||
private AlertDefineBindDao alertDefineBindDao;
|
||||
|
||||
@Autowired
|
||||
private AlertMonitorDao alertMonitorDao;
|
||||
private PeriodicAlertRuleScheduler periodicAlertRuleScheduler;
|
||||
|
||||
private final Map<String, AlertDefineImExportService> alertDefineImExportServiceMap = new HashMap<>();
|
||||
|
||||
@@ -89,29 +82,43 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
|
||||
@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());
|
||||
if (ALERT_THRESHOLD_TYPE_REALTIME.equals(alertDefine.getType())) {
|
||||
try {
|
||||
JexlExpressionRunner.compile(alertDefine.getExpr());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("alert expr error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
// the name of the alarm rule is unique
|
||||
Optional<AlertDefine> optional = alertDefineDao.findAlertDefineByName(alertDefine.getName());
|
||||
if (optional.isPresent()) {
|
||||
if (!isModify || !optional.get().getId().equals(alertDefine.getId())) {
|
||||
throw new IllegalArgumentException("alert name already exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAlertDefine(AlertDefine alertDefine) throws RuntimeException {
|
||||
alertDefineDao.save(alertDefine);
|
||||
alertDefine = alertDefineDao.save(alertDefine);
|
||||
periodicAlertRuleScheduler.updateSchedule(alertDefine);
|
||||
CacheFactory.clearAlertDefineCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyAlertDefine(AlertDefine alertDefine) throws RuntimeException {
|
||||
alertDefineDao.save(alertDefine);
|
||||
periodicAlertRuleScheduler.updateSchedule(alertDefine);
|
||||
CacheFactory.clearAlertDefineCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAlertDefine(long alertId) throws RuntimeException {
|
||||
alertDefineDao.deleteById(alertId);
|
||||
periodicAlertRuleScheduler.cancelSchedule(alertId);
|
||||
CacheFactory.clearAlertDefineCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,47 +130,14 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
@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);
|
||||
for (Long alertId : alertIds) {
|
||||
periodicAlertRuleScheduler.cancelSchedule(alertId);
|
||||
}
|
||||
// Delete all associations of this alarm
|
||||
alertDefineBindDao.deleteAlertDefineBindsByAlertDefineIdEquals(alertId);
|
||||
// Save the associated
|
||||
alertDefineBindDao.saveAll(alertDefineBinds);
|
||||
CacheFactory.clearAlertDefineCache();
|
||||
}
|
||||
|
||||
@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(List<Long> defineIds, String search, Byte priority, String sort, String order, int pageIndex, int pageSize) {
|
||||
public Page<AlertDefine> getAlertDefines(List<Long> defineIds, String search, String sort, String order, int pageIndex, int pageSize) {
|
||||
// parse translation content list
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
List<String> searchList = Collections.emptyList();
|
||||
@@ -190,10 +164,10 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
for (String searchContent : finalSearchList) {
|
||||
searchContent = searchContent.toLowerCase();
|
||||
Predicate predicate = criteriaBuilder.or(
|
||||
criteriaBuilder.like(criteriaBuilder.lower(root.get("app")), "%" + searchContent + "%"),
|
||||
criteriaBuilder.like(criteriaBuilder.lower(root.get("metric")), "%" + searchContent + "%"),
|
||||
criteriaBuilder.like(criteriaBuilder.lower(root.get("field")), "%" + searchContent + "%"),
|
||||
criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%" + searchContent + "%"),
|
||||
criteriaBuilder.like(criteriaBuilder.lower(root.get("expr")), "%" + searchContent + "%"),
|
||||
criteriaBuilder.like(criteriaBuilder.lower(root.get("labels")), "%" + searchContent + "%"),
|
||||
criteriaBuilder.like(criteriaBuilder.lower(root.get("annotations")), "%" + searchContent + "%"),
|
||||
criteriaBuilder.like(criteriaBuilder.lower(root.get("template")), "%" + searchContent + "%")
|
||||
);
|
||||
searchPredicates.add(predicate);
|
||||
@@ -201,10 +175,6 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
// all search keywords are connected with or
|
||||
andList.add(criteriaBuilder.or(searchPredicates.toArray(new Predicate[0])));
|
||||
}
|
||||
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));
|
||||
};
|
||||
@@ -213,28 +183,6 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
return alertDefineDao.findAll(specification, pageRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AlertDefineMonitorBind> getBindAlertDefineMonitors(long alertDefineId) {
|
||||
List<AlertDefineMonitorBind> defineMonitorBinds = alertDefineBindDao.getAlertDefineBindsByAlertDefineIdEquals(alertDefineId);
|
||||
if (defineMonitorBinds == null || defineMonitorBinds.isEmpty()) {
|
||||
return defineMonitorBinds;
|
||||
}
|
||||
List<Long> needLoadMonitorIds = defineMonitorBinds.stream()
|
||||
.filter(bind -> bind.getMonitor() == null)
|
||||
.map(AlertDefineMonitorBind::getMonitorId).toList();
|
||||
if (needLoadMonitorIds.isEmpty()) {
|
||||
return defineMonitorBinds;
|
||||
}
|
||||
Map<Long, Monitor> monitorMap = alertMonitorDao.findAllById(needLoadMonitorIds)
|
||||
.stream().collect(Collectors.toMap(Monitor::getId, Function.identity()));
|
||||
for (AlertDefineMonitorBind bind : defineMonitorBinds) {
|
||||
if (bind.getMonitor() == null) {
|
||||
bind.setMonitor(monitorMap.get(bind.getMonitorId()));
|
||||
}
|
||||
}
|
||||
return defineMonitorBinds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void export(List<Long> ids, String type, HttpServletResponse res) throws Exception {
|
||||
var imExportService = alertDefineImExportServiceMap.get(type);
|
||||
@@ -260,4 +208,14 @@ public class AlertDefineServiceImpl implements AlertDefineService {
|
||||
var imExportService = alertDefineImExportServiceMap.get(type);
|
||||
imExportService.importConfig(file.getInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AlertDefine> getRealTimeAlertDefines() {
|
||||
List<AlertDefine> alertDefines = CacheFactory.getAlertDefineCache();
|
||||
if (alertDefines == null) {
|
||||
alertDefines = alertDefineDao.findAlertDefinesByTypeAndEnableTrue(ALERT_THRESHOLD_TYPE_REALTIME);
|
||||
CacheFactory.setAlertDefineCache(alertDefines);
|
||||
}
|
||||
return alertDefines;
|
||||
}
|
||||
}
|
||||
|
||||
+27
-26
@@ -23,12 +23,10 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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.apache.hertzbeat.alert.dao.AlertGroupConvergeDao;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmGroupReduce;
|
||||
import org.apache.hertzbeat.alert.service.AlertGroupConvergeService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
@@ -44,42 +42,45 @@ import org.springframework.util.StringUtils;
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Slf4j
|
||||
public class AlertConvergeServiceImpl implements AlertConvergeService {
|
||||
public class AlertGroupConvergeServiceImpl implements AlertGroupConvergeService {
|
||||
|
||||
@Autowired
|
||||
private AlertConvergeDao alertConvergeDao;
|
||||
private AlertGroupConvergeDao alertGroupConvergeDao;
|
||||
|
||||
@Autowired
|
||||
private AlarmGroupReduce alarmGroupReduce;
|
||||
|
||||
@Override
|
||||
public void validate(AlertConverge alertConverge, boolean isModify) throws IllegalArgumentException {
|
||||
public void validate(AlertGroupConverge alertGroupConverge, boolean isModify) throws IllegalArgumentException {
|
||||
// todo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAlertConverge(AlertConverge alertConverge) throws RuntimeException {
|
||||
alertConvergeDao.save(alertConverge);
|
||||
clearAlertConvergesCache();
|
||||
public void addAlertGroupConverge(AlertGroupConverge alertGroupConverge) throws RuntimeException {
|
||||
alertGroupConvergeDao.save(alertGroupConverge);
|
||||
refreshAlertGroupConvergesCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyAlertConverge(AlertConverge alertConverge) throws RuntimeException {
|
||||
alertConvergeDao.save(alertConverge);
|
||||
clearAlertConvergesCache();
|
||||
public void modifyAlertGroupConverge(AlertGroupConverge alertGroupConverge) throws RuntimeException {
|
||||
alertGroupConvergeDao.save(alertGroupConverge);
|
||||
refreshAlertGroupConvergesCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertConverge getAlertConverge(long convergeId) throws RuntimeException {
|
||||
return alertConvergeDao.findById(convergeId).orElse(null);
|
||||
public AlertGroupConverge getAlertGroupConverge(long convergeId) throws RuntimeException {
|
||||
return alertGroupConvergeDao.findById(convergeId).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAlertConverges(Set<Long> convergeIds) throws RuntimeException {
|
||||
alertConvergeDao.deleteAlertConvergesByIdIn(convergeIds);
|
||||
clearAlertConvergesCache();
|
||||
public void deleteAlertGroupConverges(Set<Long> convergeIds) throws RuntimeException {
|
||||
alertGroupConvergeDao.deleteAlertGroupConvergesByIdIn(convergeIds);
|
||||
refreshAlertGroupConvergesCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<AlertConverge> getAlertConverges(List<Long> convergeIds, String search, String sort, String order, int pageIndex, int pageSize) {
|
||||
Specification<AlertConverge> specification = (root, query, criteriaBuilder) -> {
|
||||
public Page<AlertGroupConverge> getAlertGroupConverges(List<Long> convergeIds, String search, String sort, String order, int pageIndex, int pageSize) {
|
||||
Specification<AlertGroupConverge> specification = (root, query, criteriaBuilder) -> {
|
||||
List<Predicate> andList = new ArrayList<>();
|
||||
if (convergeIds != null && !convergeIds.isEmpty()) {
|
||||
CriteriaBuilder.In<Long> inPredicate = criteriaBuilder.in(root.get("id"));
|
||||
@@ -102,11 +103,11 @@ public class AlertConvergeServiceImpl implements AlertConvergeService {
|
||||
};
|
||||
Sort sortExp = Sort.by(new Sort.Order(Sort.Direction.fromString(order), sort));
|
||||
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sortExp);
|
||||
return alertConvergeDao.findAll(specification, pageRequest);
|
||||
return alertGroupConvergeDao.findAll(specification, pageRequest);
|
||||
}
|
||||
|
||||
private void clearAlertConvergesCache() {
|
||||
CommonCacheService<String, Object> convergeCache = CacheFactory.getAlertConvergeCache();
|
||||
convergeCache.remove(CommonConstants.CACHE_ALERT_CONVERGE);
|
||||
private void refreshAlertGroupConvergesCache() {
|
||||
List<AlertGroupConverge> alertGroupConverges = alertGroupConvergeDao.findAlertGroupConvergesByEnableIsTrue();
|
||||
alarmGroupReduce.refreshGroupDefines(alertGroupConverges);
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dao.AlertInhibitDao;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmInhibitReduce;
|
||||
import org.apache.hertzbeat.alert.service.AlertInhibitService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertInhibit;
|
||||
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.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* management interface service implement for alert inhibit
|
||||
*/
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Slf4j
|
||||
public class AlertInhibitServiceImpl implements AlertInhibitService {
|
||||
|
||||
@Autowired
|
||||
private AlertInhibitDao alertInhibitDao;
|
||||
|
||||
@Autowired
|
||||
private AlarmInhibitReduce alarmInhibitReduce;
|
||||
|
||||
@Override
|
||||
public void validate(AlertInhibit alertInhibit, boolean isModify) throws IllegalArgumentException {
|
||||
// todo
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAlertInhibit(AlertInhibit alertInhibit) throws RuntimeException {
|
||||
alertInhibitDao.save(alertInhibit);
|
||||
refreshAlertInhibitsCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyAlertInhibit(AlertInhibit alertInhibit) throws RuntimeException {
|
||||
alertInhibitDao.save(alertInhibit);
|
||||
refreshAlertInhibitsCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertInhibit getAlertInhibit(long inhibitId) throws RuntimeException {
|
||||
return alertInhibitDao.findById(inhibitId).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAlertInhibits(Set<Long> inhibitIds) throws RuntimeException {
|
||||
alertInhibitDao.deleteAlertInhibitsByIdIn(inhibitIds);
|
||||
refreshAlertInhibitsCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<AlertInhibit> getAlertInhibits(List<Long> inhibitIds, String search, String sort, String order, int pageIndex, int pageSize) {
|
||||
Specification<AlertInhibit> specification = (root, query, criteriaBuilder) -> {
|
||||
List<Predicate> andList = new ArrayList<>();
|
||||
if (inhibitIds != null && !inhibitIds.isEmpty()) {
|
||||
CriteriaBuilder.In<Long> inPredicate = criteriaBuilder.in(root.get("id"));
|
||||
for (long id : inhibitIds) {
|
||||
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);
|
||||
return alertInhibitDao.findAll(specification, pageRequest);
|
||||
}
|
||||
|
||||
private void refreshAlertInhibitsCache() {
|
||||
alarmInhibitReduce.refreshInhibitRules(alertInhibitDao.findAlertInhibitsByEnableIsTrue());
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dto.AlertManagerExternAlert;
|
||||
import org.apache.hertzbeat.alert.dto.PrometheusExternAlert;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
|
||||
import org.apache.hertzbeat.alert.service.ExternAlertService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Alertmanager external alarm service impl
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AlertManagerExternAlertService implements ExternAlertService {
|
||||
|
||||
@Autowired
|
||||
private AlarmCommonReduce alarmCommonReduce;
|
||||
|
||||
|
||||
@Override
|
||||
public void addExternAlert(String content) {
|
||||
|
||||
AlertManagerExternAlert alert = JsonUtil.fromJson(content, AlertManagerExternAlert.class);
|
||||
if (alert == null) {
|
||||
log.warn("parse alertmanager extern alert content failed! content: {}", content);
|
||||
return;
|
||||
}
|
||||
List<PrometheusExternAlert> alerts = alert.getAlerts();
|
||||
if (alerts == null || alerts.isEmpty()) {
|
||||
log.warn("receive alertmanager extern alert without alerts! content: {}", content);
|
||||
return;
|
||||
}
|
||||
for (PrometheusExternAlert prometheusAlert : alerts) {
|
||||
Map<String, String> annotations = prometheusAlert.getAnnotations();
|
||||
if (annotations == null) {
|
||||
annotations = new HashMap<>(8);
|
||||
}
|
||||
if (StringUtils.hasText(prometheusAlert.getGeneratorURL())) {
|
||||
annotations.put("generatorURL", prometheusAlert.getGeneratorURL());
|
||||
}
|
||||
String description = annotations.get("description");
|
||||
if (description == null) {
|
||||
description = annotations.get("summary");
|
||||
}
|
||||
if (description == null) {
|
||||
description = annotations.values().stream().findFirst().orElse("");
|
||||
}
|
||||
Map<String, String> labels = prometheusAlert.getLabels();
|
||||
if (labels == null) {
|
||||
labels = new HashMap<>(8);
|
||||
}
|
||||
labels.put("__source__", "alertmanager");
|
||||
String status = CommonConstants.ALERT_STATUS_FIRING;
|
||||
if (prometheusAlert.getEndsAt() != null && prometheusAlert.getEndsAt().isBefore(Instant.now())) {
|
||||
status = CommonConstants.ALERT_STATUS_RESOLVED;
|
||||
}
|
||||
SingleAlert singleAlert = SingleAlert.builder()
|
||||
.content(description)
|
||||
.status(status)
|
||||
.activeAt(CommonConstants.ALERT_STATUS_FIRING.equals(status) ? Instant.now().toEpochMilli() : null)
|
||||
.startAt(prometheusAlert.getStartsAt() != null ? prometheusAlert.getStartsAt().toEpochMilli() : Instant.now().toEpochMilli())
|
||||
.endAt(CommonConstants.ALERT_STATUS_RESOLVED.equals(status) ? prometheusAlert.getEndsAt().toEpochMilli() : null)
|
||||
.labels(labels)
|
||||
.annotations(prometheusAlert.getAnnotations())
|
||||
.triggerTimes(1)
|
||||
.build();
|
||||
|
||||
alarmCommonReduce.reduceAndSendAlarm(singleAlert);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportSource() {
|
||||
return "alertmanager";
|
||||
}
|
||||
}
|
||||
+112
-141
@@ -17,30 +17,21 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dao.AlertDao;
|
||||
import org.apache.hertzbeat.alert.dto.AlertPriorityNum;
|
||||
import org.apache.hertzbeat.alert.dao.GroupAlertDao;
|
||||
import org.apache.hertzbeat.alert.dao.SingleAlertDao;
|
||||
import org.apache.hertzbeat.alert.dto.AlertSummary;
|
||||
import org.apache.hertzbeat.alert.dto.CloudAlertReportAbstract;
|
||||
import org.apache.hertzbeat.alert.enums.CloudServiceAlarmInformationEnum;
|
||||
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.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
@@ -56,95 +47,153 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Slf4j
|
||||
public class AlertServiceImpl implements AlertService {
|
||||
|
||||
|
||||
@Autowired
|
||||
private AlertDao alertDao;
|
||||
private GroupAlertDao groupAlertDao;
|
||||
|
||||
@Autowired
|
||||
private SingleAlertDao singleAlertDao;
|
||||
|
||||
@Autowired
|
||||
private AlarmCommonReduce alarmCommonReduce;
|
||||
|
||||
@Override
|
||||
public void addAlert(Alert alert) throws RuntimeException {
|
||||
alertDao.save(alert);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Alert> getAlerts(List<Long> alarmIds, Long monitorId, Byte priority, Byte status, String content, String sort, String order, int pageIndex, int pageSize) {
|
||||
Specification<Alert> specification = (root, query, criteriaBuilder) -> {
|
||||
public Page<SingleAlert> getSingleAlerts(String status, String search, String sort, String order, int pageIndex, int pageSize) {
|
||||
Specification<SingleAlert> specification = (root, query, criteriaBuilder) -> {
|
||||
List<Predicate> andList = new ArrayList<>();
|
||||
|
||||
if (alarmIds != null && !alarmIds.isEmpty()) {
|
||||
CriteriaBuilder.In<Long> inPredicate = criteriaBuilder.in(root.get("id"));
|
||||
for (long id : alarmIds) {
|
||||
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[] andPredicates = new Predicate[andList.size()];
|
||||
Predicate andPredicate = criteriaBuilder.and(andList.toArray(andPredicates));
|
||||
List<Predicate> orList = new ArrayList<>();
|
||||
if (search != null && !search.isEmpty()) {
|
||||
Predicate predicateContent = criteriaBuilder.like(root.get("content"), "%" + search + "%");
|
||||
orList.add(predicateContent);
|
||||
Predicate predicateLabels = criteriaBuilder.like(root.get("labels"), "%" + search + "%");
|
||||
orList.add(predicateLabels);
|
||||
Predicate predicateAnnotation = criteriaBuilder.like(root.get("annotations"), "%" + search + "%");
|
||||
orList.add(predicateAnnotation);
|
||||
}
|
||||
Predicate[] orPredicates = new Predicate[orList.size()];
|
||||
Predicate orPredicate = criteriaBuilder.or(orList.toArray(orPredicates));
|
||||
if (andPredicates.length == 0 && orPredicates.length == 0) {
|
||||
return query.where().getRestriction();
|
||||
} else if (andPredicates.length == 0) {
|
||||
return orPredicate;
|
||||
} else if (orPredicates.length == 0) {
|
||||
return andPredicate;
|
||||
} else {
|
||||
return query.where(andPredicate, orPredicate).getRestriction();
|
||||
}
|
||||
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);
|
||||
return alertDao.findAll(specification, pageRequest);
|
||||
return singleAlertDao.findAll(specification, pageRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAlerts(HashSet<Long> ids) {
|
||||
alertDao.deleteAlertsByIdIn(ids);
|
||||
public Page<GroupAlert> getGroupAlerts(String status, String search, String sort, String order, int pageIndex, int pageSize) {
|
||||
Specification<GroupAlert> specification = (root, query, criteriaBuilder) -> {
|
||||
List<Predicate> andList = new ArrayList<>();
|
||||
if (status != null) {
|
||||
Predicate predicate = criteriaBuilder.equal(root.get("status"), status);
|
||||
andList.add(predicate);
|
||||
}
|
||||
Predicate[] andPredicates = new Predicate[andList.size()];
|
||||
Predicate andPredicate = criteriaBuilder.and(andList.toArray(andPredicates));
|
||||
List<Predicate> orList = new ArrayList<>();
|
||||
if (search != null && !search.isEmpty()) {
|
||||
Predicate predicateContent = criteriaBuilder.like(root.get("groupLabels"), "%" + search + "%");
|
||||
orList.add(predicateContent);
|
||||
Predicate predicateLabels = criteriaBuilder.like(root.get("commonLabels"), "%" + search + "%");
|
||||
orList.add(predicateLabels);
|
||||
Predicate predicateAnnotation = criteriaBuilder.like(root.get("commonAnnotations"), "%" + search + "%");
|
||||
orList.add(predicateAnnotation);
|
||||
}
|
||||
Predicate[] orPredicates = new Predicate[orList.size()];
|
||||
Predicate orPredicate = criteriaBuilder.or(orList.toArray(orPredicates));
|
||||
if (andPredicates.length == 0 && orPredicates.length == 0) {
|
||||
return query.where().getRestriction();
|
||||
} else if (andPredicates.length == 0) {
|
||||
return orPredicate;
|
||||
} else if (orPredicates.length == 0) {
|
||||
return andPredicate;
|
||||
} else {
|
||||
return query.where(andPredicate, orPredicate).getRestriction();
|
||||
}
|
||||
};
|
||||
Sort sortExp = Sort.by(new Sort.Order(Sort.Direction.fromString(order), sort));
|
||||
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sortExp);
|
||||
Page<GroupAlert> groupAlertPage = groupAlertDao.findAll(specification, pageRequest);
|
||||
for (GroupAlert groupAlert : groupAlertPage.getContent()) {
|
||||
List<String> firingAlerts = groupAlert.getAlertFingerprints();
|
||||
List<SingleAlert> singleAlerts = singleAlertDao.findSingleAlertsByFingerprintIn(firingAlerts);
|
||||
groupAlert.setAlerts(singleAlerts);
|
||||
}
|
||||
return groupAlertPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAlerts() {
|
||||
alertDao.deleteAll();
|
||||
public void deleteGroupAlerts(HashSet<Long> ids) {
|
||||
List<GroupAlert> groupAlerts = groupAlertDao.findGroupAlertsByIdIn(ids);
|
||||
for (GroupAlert groupAlert : groupAlerts) {
|
||||
List<String> firingAlerts = groupAlert.getAlertFingerprints();
|
||||
singleAlertDao.deleteSingleAlertsByFingerprintIn(firingAlerts);
|
||||
}
|
||||
groupAlertDao.deleteGroupAlertsByIdIn(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editAlertStatus(Byte status, List<Long> ids) {
|
||||
alertDao.updateAlertsStatus(status, ids);
|
||||
public void deleteSingleAlerts(HashSet<Long> ids) {
|
||||
singleAlertDao.deleteSingleAlertsByIdIn(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editGroupAlertStatus(String status, List<Long> ids) {
|
||||
groupAlertDao.updateGroupAlertsStatus(status, ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editSingleAlertStatus(String status, List<Long> ids) {
|
||||
singleAlertDao.updateSingleAlertsStatus(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());
|
||||
List<SingleAlert> firingAlerts = singleAlertDao.querySingleAlertsByStatus(CommonConstants.ALERT_STATUS_FIRING);
|
||||
// severity - emergency critical warning info
|
||||
int emergencyNum = 0;
|
||||
int criticalNum = 0;
|
||||
int warningNum = 0;
|
||||
for (SingleAlert alert : firingAlerts) {
|
||||
String severity = alert.getLabels().get(CommonConstants.LABEL_ALERT_SEVERITY);
|
||||
if (severity != null) {
|
||||
switch (severity) {
|
||||
case CommonConstants.ALERT_SEVERITY_EMERGENCY -> emergencyNum++;
|
||||
case CommonConstants.ALERT_SEVERITY_CRITICAL -> criticalNum++;
|
||||
case CommonConstants.ALERT_SEVERITY_WARNING -> warningNum++;
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
alertSummary.setPriorityCriticalNum(criticalNum);
|
||||
alertSummary.setPriorityEmergencyNum(emergencyNum);
|
||||
alertSummary.setPriorityWarningNum(warningNum);
|
||||
}
|
||||
long total = alertDao.count();
|
||||
|
||||
long total = singleAlertDao.count();
|
||||
alertSummary.setTotal(total);
|
||||
long dealNum = total - alertSummary.getPriorityCriticalNum()
|
||||
- alertSummary.getPriorityEmergencyNum() - alertSummary.getPriorityWarningNum();
|
||||
alertSummary.setDealNum(dealNum);
|
||||
long resolved = total - firingAlerts.size();
|
||||
alertSummary.setDealNum(resolved);
|
||||
try {
|
||||
if (total == 0) {
|
||||
alertSummary.setRate(100);
|
||||
} else {
|
||||
float rate = BigDecimal.valueOf(100 * (float) dealNum / total)
|
||||
float rate = BigDecimal.valueOf(100 * (float) resolved / total)
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
.floatValue();
|
||||
alertSummary.setRate(rate);
|
||||
@@ -154,82 +203,4 @@ public class AlertServiceImpl implements AlertService {
|
||||
}
|
||||
return alertSummary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNewAlertReport(AlertReport alertReport) {
|
||||
alarmCommonReduce.reduceAndSendAlarm(buildAlertData(alertReport));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNewAlertReportFromCloud(String cloudServiceName, 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(Instant.now().getEpochSecond())
|
||||
.priority(1)
|
||||
.reportType(1)
|
||||
.build();
|
||||
}
|
||||
Optional.ofNullable(alert).ifPresent(this::addNewAlertReport);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-4
@@ -27,8 +27,6 @@ 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;
|
||||
@@ -111,7 +109,6 @@ public class AlertSilenceServiceImpl implements AlertSilenceService {
|
||||
}
|
||||
|
||||
private void clearAlertSilencesCache() {
|
||||
CommonCacheService<String, Object> silenceCache = CacheFactory.getAlertSilenceCache();
|
||||
silenceCache.remove(CommonConstants.CACHE_ALERT_SILENCE);
|
||||
CacheFactory.clearAlertSilenceCache();
|
||||
}
|
||||
}
|
||||
|
||||
+347
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* 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.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Stack;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.service.DataSourceService;
|
||||
import org.apache.hertzbeat.warehouse.db.QueryExecutor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* datasource service
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class DataSourceServiceImpl implements DataSourceService {
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<QueryExecutor> executors;
|
||||
|
||||
private static final Pattern EXPR_TOKEN = Pattern.compile("\\(|\\)|[a-zA-Z_][a-zA-Z0-9_=~{}\\[\\]\".]*|\\d+(\\.\\d+)?|>=|<=|==|!=|>|<|and|or|unless");
|
||||
private static final String THRESHOLD = "__threshold__";
|
||||
private static final String VALUE = "__value__";
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> calculate(String datasource, String expr) {
|
||||
if (!StringUtils.hasText(expr)) {
|
||||
throw new IllegalArgumentException("Empty expression");
|
||||
}
|
||||
if (executors == null || executors.isEmpty()) {
|
||||
throw new IllegalArgumentException("No query executor found");
|
||||
}
|
||||
QueryExecutor executor = executors.stream().filter(e -> e.support(datasource)).findFirst().orElse(null);
|
||||
if (executor == null) {
|
||||
throw new IllegalArgumentException("Unsupported datasource: " + datasource);
|
||||
}
|
||||
// replace all white space
|
||||
expr = expr.replaceAll("\\s+", " ");
|
||||
try {
|
||||
return evaluate(expr, executor);
|
||||
} catch (Exception e) {
|
||||
log.error("Error executing query on datasource {}: {}", datasource, e.getMessage());
|
||||
throw new RuntimeException("Query execution failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> evaluate(String expr, QueryExecutor executor) {
|
||||
Stack<List<Map<String, Object>>> values = new Stack<>();
|
||||
Stack<String> operators = new Stack<>();
|
||||
Matcher matcher = EXPR_TOKEN.matcher(expr);
|
||||
List<String> tokens = new ArrayList<>();
|
||||
while (matcher.find()) {
|
||||
tokens.add(matcher.group());
|
||||
}
|
||||
for (String token : tokens) {
|
||||
if (token.equals("(")) {
|
||||
operators.push(token);
|
||||
} else if (token.equals(")")) {
|
||||
while (!operators.isEmpty() && !operators.peek().equals("(")) {
|
||||
applyOperator(values, operators.pop());
|
||||
}
|
||||
// remove the left parenthesis
|
||||
operators.pop();
|
||||
} else if (token.matches(">=|<=|==|!=|>|<")) {
|
||||
operators.push(token);
|
||||
} else if (token.equals("and") || token.equals("or") || token.equals("unless")) {
|
||||
while (!operators.isEmpty() && precedence(operators.peek()) >= precedence(token)) {
|
||||
applyOperator(values, operators.pop());
|
||||
}
|
||||
operators.push(token);
|
||||
} else if (token.matches("\\d+(\\.\\d+)?")) {
|
||||
double value = Double.parseDouble(token);
|
||||
List<Map<String, Object>> numAsList = new ArrayList<>();
|
||||
numAsList.add(Map.of(THRESHOLD, value));
|
||||
values.push(numAsList);
|
||||
} else if (token.matches("[a-zA-Z_][a-zA-Z0-9_=~{}\\[\\]\".]*")) {
|
||||
List<Map<String, Object>> results = executor.execute(token);
|
||||
values.push(results);
|
||||
}
|
||||
}
|
||||
while (!operators.isEmpty()) {
|
||||
applyOperator(values, operators.pop());
|
||||
}
|
||||
return values.isEmpty() ? new LinkedList<>() : values.pop();
|
||||
}
|
||||
|
||||
private int precedence(String op) {
|
||||
return switch (op) {
|
||||
case "or" -> 1;
|
||||
case "unless" -> 2;
|
||||
case "and" -> 3;
|
||||
case ">", "<", ">=", "<=", "==", "!=" -> 4;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
private void applyOperator(Stack<List<Map<String, Object>>> values, String op) {
|
||||
if (values.size() < 2) {
|
||||
return;
|
||||
};
|
||||
List<Map<String, Object>> rightOperand = values.pop();
|
||||
List<Map<String, Object>> leftOperand = values.pop();
|
||||
if (rightOperand.size() == 1 && rightOperand.get(0).containsKey(THRESHOLD)) {
|
||||
double threshold = (double) rightOperand.get(0).get(THRESHOLD);
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (Map<String, Object> item : leftOperand) {
|
||||
Object queryValues = item.get(VALUE);
|
||||
if (queryValues == null) {
|
||||
// ignore the query result data is empty
|
||||
continue;
|
||||
}
|
||||
// queryValues may be a list of values, or a single value
|
||||
Object matchValue = evaluateCondition(queryValues, op, threshold);
|
||||
item.put(VALUE, matchValue);
|
||||
// if matchValue is null, mean not match the threshold
|
||||
// if not null, mean match the threshold
|
||||
result.add(new HashMap<>(item));
|
||||
}
|
||||
if (!result.isEmpty()) {
|
||||
values.push(result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Map<String, Object> leftMap = null;
|
||||
boolean leftMatch = false;
|
||||
Map<String, Object> rightMap = null;
|
||||
boolean rightMatch = false;
|
||||
switch (op) {
|
||||
case "and" -> {
|
||||
for (Map<String, Object> item : leftOperand) {
|
||||
if (leftMap == null) {
|
||||
leftMap = item;
|
||||
}
|
||||
if (item.get(VALUE) != null) {
|
||||
leftMap = item;
|
||||
leftMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (Map<String, Object> item : rightOperand) {
|
||||
if (rightMap == null) {
|
||||
rightMap = item;
|
||||
}
|
||||
if (item.get(VALUE) != null) {
|
||||
rightMap = item;
|
||||
rightMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (leftMatch && rightMatch) {
|
||||
rightMap.putAll(leftMap);
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
} else if (leftMap != null) {
|
||||
leftMap.put(VALUE, null);
|
||||
values.push(new LinkedList<>(List.of(leftMap)));
|
||||
} else if (rightMap != null) {
|
||||
rightMap.put(VALUE, null);
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
}
|
||||
}
|
||||
case "or" -> {
|
||||
for (Map<String, Object> item : leftOperand) {
|
||||
if (leftMap == null) {
|
||||
leftMap = item;
|
||||
}
|
||||
if (item.get(VALUE) != null) {
|
||||
leftMap = item;
|
||||
leftMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (Map<String, Object> item : rightOperand) {
|
||||
if (rightMap == null) {
|
||||
rightMap = item;
|
||||
}
|
||||
if (item.get(VALUE) != null) {
|
||||
rightMap = item;
|
||||
rightMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (leftMatch && rightMatch) {
|
||||
rightMap.putAll(leftMap);
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
} else if (leftMatch) {
|
||||
values.push(new LinkedList<>(List.of(leftMap)));
|
||||
} else if (rightMatch) {
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
} else {
|
||||
if (leftMap != null && rightMap != null) {
|
||||
rightMap.putAll(leftMap);
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
} else if (leftMap != null) {
|
||||
values.push(new LinkedList<>(List.of(leftMap)));
|
||||
} else if (rightMap != null){
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
}
|
||||
}
|
||||
}
|
||||
case "unless" -> {
|
||||
for (Map<String, Object> item : leftOperand) {
|
||||
if (leftMap == null) {
|
||||
leftMap = item;
|
||||
}
|
||||
if (item.get(VALUE) != null) {
|
||||
leftMap = item;
|
||||
leftMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (Map<String, Object> item : rightOperand) {
|
||||
if (rightMap == null) {
|
||||
rightMap = item;
|
||||
}
|
||||
if (item.get(VALUE) != null) {
|
||||
rightMap = item;
|
||||
rightMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (leftMatch && !rightMatch) {
|
||||
values.push(new LinkedList<>(List.of(leftMap)));
|
||||
} else {
|
||||
if (leftMap != null) {
|
||||
leftMap.put(VALUE, null);
|
||||
values.push(new LinkedList<>(List.of(leftMap)));
|
||||
} else {
|
||||
if (rightMap != null) {
|
||||
rightMap.put(VALUE, null);
|
||||
values.push(new LinkedList<>(List.of(rightMap)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default -> throw new IllegalArgumentException("Unsupported operator: " + op);
|
||||
}
|
||||
}
|
||||
|
||||
private Object evaluateCondition(Object value, String operator, Double threshold) {
|
||||
// value may be a list of values, or a single value
|
||||
switch (operator) {
|
||||
case ">":
|
||||
// if value is list, return the max value
|
||||
if (value instanceof List<?> values) {
|
||||
Double doubleValue = values.stream().map(v -> Double.valueOf(v.toString()))
|
||||
.max(Double::compareTo).orElse(null);
|
||||
if (doubleValue != null) {
|
||||
return doubleValue > threshold ? doubleValue : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return Double.parseDouble(value.toString()) > threshold ? value : null;
|
||||
}
|
||||
case ">=":
|
||||
if (value instanceof List<?> values) {
|
||||
Double doubleValue = values.stream().map(v -> Double.valueOf(v.toString()))
|
||||
.max(Double::compareTo).orElse(null);
|
||||
if (doubleValue != null) {
|
||||
return doubleValue >= threshold ? doubleValue : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return Double.parseDouble(value.toString()) >= threshold ? value : null;
|
||||
}
|
||||
case "<":
|
||||
if (value instanceof List<?> values) {
|
||||
Double doubleValue = values.stream().map(v -> Double.valueOf(v.toString()))
|
||||
.min(Double::compareTo).orElse(null);
|
||||
if (doubleValue != null) {
|
||||
return doubleValue < threshold ? doubleValue : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return Double.parseDouble(value.toString()) < threshold ? value : null;
|
||||
}
|
||||
case "<=":
|
||||
if (value instanceof List<?> values) {
|
||||
Double doubleValue = values.stream().map(v -> Double.valueOf(v.toString()))
|
||||
.min(Double::compareTo).orElse(null);
|
||||
if (doubleValue != null) {
|
||||
return doubleValue <= threshold ? doubleValue : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return Double.parseDouble(value.toString()) <= threshold ? value : null;
|
||||
}
|
||||
case "==":
|
||||
if (value instanceof List<?> values) {
|
||||
for (Object v : values) {
|
||||
if (v.equals(threshold)) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return value.equals(threshold) ? value : null;
|
||||
}
|
||||
case "!=":
|
||||
if (value instanceof List<?> values) {
|
||||
for (Object v : values) {
|
||||
if (v.equals(threshold)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
} else {
|
||||
return value.equals(threshold) ? null : value;
|
||||
}
|
||||
default:
|
||||
// unsupported operator todo add more operator
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setExecutors(List<QueryExecutor> mockExecutor) {
|
||||
this.executors = mockExecutor;
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import java.time.Instant;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
|
||||
import org.apache.hertzbeat.alert.service.ExternAlertService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Default external alarm service impl
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class DefaultExternAlertService implements ExternAlertService {
|
||||
|
||||
@Autowired
|
||||
private AlarmCommonReduce alarmCommonReduce;
|
||||
|
||||
@Override
|
||||
public void addExternAlert(String content) {
|
||||
SingleAlert alert = JsonUtil.fromJson(content, SingleAlert.class);
|
||||
if (alert == null) {
|
||||
log.warn("parse extern alert content failed! content: {}", content);
|
||||
throw new IllegalArgumentException("parse extern alert content failed!");
|
||||
}
|
||||
alert.setId(null);
|
||||
String status = alert.getStatus();
|
||||
if (status == null) {
|
||||
// can use the startAt and endAt to determine the status
|
||||
if (alert.getStartAt() != null && alert.getEndAt() != null) {
|
||||
if (alert.getEndAt() < Instant.now().toEpochMilli()) {
|
||||
status = CommonConstants.ALERT_STATUS_RESOLVED;
|
||||
} else {
|
||||
status = CommonConstants.ALERT_STATUS_FIRING;
|
||||
}
|
||||
} else {
|
||||
status = CommonConstants.ALERT_STATUS_FIRING;
|
||||
}
|
||||
}
|
||||
alert.setStatus(status);
|
||||
if (CommonConstants.ALERT_STATUS_FIRING.equals(status)) {
|
||||
alert.setEndAt(null);
|
||||
if (alert.getStartAt() == null) {
|
||||
alert.setStartAt(Instant.now().toEpochMilli());
|
||||
}
|
||||
if (alert.getActiveAt() == null) {
|
||||
alert.setActiveAt(Instant.now().toEpochMilli());
|
||||
}
|
||||
} else {
|
||||
alert.setActiveAt(null);
|
||||
if (alert.getStartAt() == null) {
|
||||
alert.setStartAt(Instant.now().toEpochMilli());
|
||||
}
|
||||
if (alert.getEndAt() == null) {
|
||||
alert.setEndAt(Instant.now().toEpochMilli());
|
||||
}
|
||||
}
|
||||
alarmCommonReduce.reduceAndSendAlarm(alert);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportSource() {
|
||||
return "default";
|
||||
}
|
||||
}
|
||||
+134
-84
@@ -15,36 +15,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.service.impl;
|
||||
package org.apache.hertzbeat.alert.service.impl;
|
||||
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
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.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.manager.component.alerter.DispatcherAlarm;
|
||||
import org.apache.hertzbeat.manager.dao.NoticeReceiverDao;
|
||||
import org.apache.hertzbeat.manager.dao.NoticeRuleDao;
|
||||
import org.apache.hertzbeat.manager.dao.NoticeTemplateDao;
|
||||
import org.apache.hertzbeat.manager.service.NoticeConfigService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.alert.notice.AlertNoticeDispatch;
|
||||
import org.apache.hertzbeat.alert.dao.NoticeReceiverDao;
|
||||
import org.apache.hertzbeat.alert.dao.NoticeRuleDao;
|
||||
import org.apache.hertzbeat.alert.dao.NoticeTemplateDao;
|
||||
import org.apache.hertzbeat.alert.service.NoticeConfigService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
@@ -52,10 +39,28 @@ import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
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.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
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.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Message notification configuration implementation
|
||||
*/
|
||||
@@ -64,11 +69,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Slf4j
|
||||
public class NoticeConfigServiceImpl implements NoticeConfigService, CommandLineRunner {
|
||||
|
||||
private static final String ALERT_TEST_TARGET = "Test Target";
|
||||
|
||||
private static final String ALERT_TEST_CONTENT = "test send msg! \\n This is the test data. It is proved that it can be received successfully";
|
||||
|
||||
|
||||
private static final Map<Byte, NoticeTemplate> PRESET_TEMPLATE = new HashMap<>(16);
|
||||
|
||||
@Autowired
|
||||
@@ -82,47 +83,90 @@ public class NoticeConfigServiceImpl implements NoticeConfigService, CommandLine
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private DispatcherAlarm dispatcherAlarm;
|
||||
private AlertNoticeDispatch dispatcherAlarm;
|
||||
|
||||
@Override
|
||||
public List<NoticeReceiver> getNoticeReceivers(String name) {
|
||||
public Page<NoticeReceiver> getNoticeReceivers(String name, int pageIndex, int pageSize) {
|
||||
Specification<NoticeReceiver> specification = (root, query, criteriaBuilder) -> {
|
||||
Predicate predicate = criteriaBuilder.conjunction();
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
|
||||
Predicate predicateName = criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%"
|
||||
);
|
||||
predicate = criteriaBuilder.and(predicateName);
|
||||
}
|
||||
return predicate;
|
||||
};
|
||||
return noticeReceiverDao.findAll(specification);
|
||||
return noticeReceiverDao.findAll(specification, PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Direction.DESC, "id")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NoticeTemplate> getNoticeTemplates(String name) {
|
||||
Specification<NoticeTemplate> specification = (root, query, criteriaBuilder) -> {
|
||||
Predicate predicate = criteriaBuilder.conjunction();
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
|
||||
predicate = criteriaBuilder.and(predicateName);
|
||||
public List<NoticeReceiver> getAllNoticeReceivers() {
|
||||
return noticeReceiverDao.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<NoticeTemplate> getNoticeTemplates(String name, boolean preset, int pageIndex, int pageSize) {
|
||||
if (preset) {
|
||||
// Query preset templates
|
||||
List<NoticeTemplate> defaultTemplates = new LinkedList<>(PRESET_TEMPLATE.values());
|
||||
|
||||
// Filter by name (case-insensitive)
|
||||
List<NoticeTemplate> filteredDefaultTemplates = defaultTemplates.stream()
|
||||
.filter(template -> StringUtils.isBlank(name)
|
||||
|| template.getName().toLowerCase().contains(name.toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Pagination logic
|
||||
int totalItems = filteredDefaultTemplates.size();
|
||||
int fromIndex = Math.min(pageIndex * pageSize, totalItems);
|
||||
int toIndex = Math.min(fromIndex + pageSize, totalItems);
|
||||
|
||||
if (fromIndex >= totalItems) {
|
||||
return new PageImpl<>(Collections.emptyList(), PageRequest.of(pageIndex, pageSize), totalItems);
|
||||
}
|
||||
return predicate;
|
||||
};
|
||||
|
||||
List<NoticeTemplate> paginatedTemplates = filteredDefaultTemplates.subList(fromIndex, toIndex);
|
||||
return new PageImpl<>(paginatedTemplates, PageRequest.of(pageIndex, pageSize), totalItems);
|
||||
} else {
|
||||
// Query custom templates
|
||||
Specification<NoticeTemplate> specification = (root, query, criteriaBuilder) -> {
|
||||
Predicate predicate = criteriaBuilder.conjunction();
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
Predicate predicateName = criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%"
|
||||
);
|
||||
predicate = criteriaBuilder.and(predicateName);
|
||||
}
|
||||
return predicate;
|
||||
};
|
||||
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Direction.DESC, "id"));
|
||||
return noticeTemplateDao.findAll(specification, pageRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public List<NoticeTemplate> getAllNoticeTemplates() {
|
||||
List<NoticeTemplate> defaultTemplates = new LinkedList<>(PRESET_TEMPLATE.values());
|
||||
defaultTemplates.addAll(noticeTemplateDao.findAll(specification));
|
||||
defaultTemplates.addAll(noticeTemplateDao.findAll());
|
||||
return defaultTemplates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NoticeRule> getNoticeRules(String name) {
|
||||
public Page<NoticeRule> getNoticeRules(String name, int pageIndex, int pageSize) {
|
||||
Specification<NoticeRule> specification = (root, query, criteriaBuilder) -> {
|
||||
Predicate predicate = criteriaBuilder.conjunction();
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
Predicate predicateName = criteriaBuilder.like(root.get("name"), "%" + name + "%");
|
||||
Predicate predicateName = criteriaBuilder.like(
|
||||
criteriaBuilder.lower(root.get("name")), "%" + name.toLowerCase() + "%"
|
||||
);
|
||||
predicate = criteriaBuilder.and(predicateName);
|
||||
}
|
||||
return predicate;
|
||||
};
|
||||
return noticeRuleDao.findAll(specification);
|
||||
return noticeRuleDao.findAll(specification, PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Direction.DESC, "id")));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,37 +203,28 @@ public class NoticeConfigServiceImpl implements NoticeConfigService, CommandLine
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<NoticeRule> getReceiverFilterRule(Alert alert) {
|
||||
public List<NoticeRule> getReceiverFilterRule(GroupAlert alert) {
|
||||
// use cache
|
||||
CommonCacheService<String, Object> noticeCache = CacheFactory.getNoticeCache();
|
||||
List<NoticeRule> rules = (List<NoticeRule>) noticeCache.get(CommonConstants.CACHE_NOTICE_RULE);
|
||||
if (CollectionUtils.isEmpty(rules)) {
|
||||
List<NoticeRule> rules = CacheFactory.getNoticeCache();
|
||||
if (rules == null) {
|
||||
rules = noticeRuleDao.findNoticeRulesByEnableTrue();
|
||||
noticeCache.put(CommonConstants.CACHE_NOTICE_RULE, rules);
|
||||
CacheFactory.setNoticeCache(rules);
|
||||
}
|
||||
|
||||
// The temporary rule is to forward all, and then implement more matching rules: alarm status selection, monitoring type selection, etc.
|
||||
return rules.stream()
|
||||
.filter(rule -> {
|
||||
if (!rule.isFilterAll()) {
|
||||
// filter priorities
|
||||
if (rule.getPriorities() != null && !rule.getPriorities().isEmpty()) {
|
||||
boolean priorityMatch = rule.getPriorities().stream().anyMatch(item -> item != null && item == alert.getPriority());
|
||||
if (!priorityMatch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// filter tags
|
||||
if (rule.getTags() != null && !rule.getTags().isEmpty()) {
|
||||
boolean tagMatch = rule.getTags().stream().anyMatch(tagItem -> {
|
||||
if (!alert.getTags().containsKey(tagItem.getName())) {
|
||||
// filter labels
|
||||
if (rule.getLabels() != null && !rule.getLabels().isEmpty()) {
|
||||
boolean labelMatch = rule.getLabels().entrySet().stream().allMatch(labelItem -> {
|
||||
if (!alert.getCommonLabels().containsKey(labelItem.getKey())) {
|
||||
return false;
|
||||
}
|
||||
String alertTagValue = alert.getTags().get(tagItem.getName());
|
||||
return Objects.equals(tagItem.getValue(), alertTagValue);
|
||||
String alertLabelValue = alert.getCommonLabels().get(labelItem.getKey());
|
||||
return Objects.equals(labelItem.getValue(), alertLabelValue);
|
||||
});
|
||||
if (!tagMatch) {
|
||||
if (!labelMatch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -267,28 +302,43 @@ public class NoticeConfigServiceImpl implements NoticeConfigService, CommandLine
|
||||
|
||||
@Override
|
||||
public boolean sendTestMsg(NoticeReceiver noticeReceiver) {
|
||||
Map<String, String> tags = new HashMap<>(8);
|
||||
tags.put(CommonConstants.TAG_MONITOR_ID, "100");
|
||||
tags.put(CommonConstants.TAG_MONITOR_NAME, "100Name");
|
||||
tags.put(CommonConstants.TAG_MONITOR_HOST, "127.0.0.1");
|
||||
tags.put(CommonConstants.TAG_THRESHOLD_ID, "200");
|
||||
Alert alert = Alert.builder()
|
||||
.tags(tags)
|
||||
.id(1003445L)
|
||||
.target(ALERT_TEST_TARGET)
|
||||
.priority(CommonConstants.ALERT_PRIORITY_CODE_CRITICAL)
|
||||
.content(ALERT_TEST_CONTENT)
|
||||
.alertDefineId(200L)
|
||||
.times(2)
|
||||
.status((byte) 0)
|
||||
.firstAlarmTime(System.currentTimeMillis())
|
||||
.lastAlarmTime(System.currentTimeMillis()).build();
|
||||
return dispatcherAlarm.sendNoticeMsg(noticeReceiver, null, alert);
|
||||
Map<String, String> labels = new HashMap<>(8);
|
||||
labels.put(CommonConstants.LABEL_INSTANCE, "1000000");
|
||||
labels.put(CommonConstants.LABEL_ALERT_NAME, "CPU Usage Alert");
|
||||
labels.put(CommonConstants.LABEL_HOST, "127.0.0.1");
|
||||
Map<String, String> annotations = new HashMap<>(8);
|
||||
annotations.put("suggest", "Please check the CPU usage of the server");
|
||||
SingleAlert singleAlert1 = SingleAlert.builder()
|
||||
.labels(labels)
|
||||
.content("test send msg! \\n This is the test data. It is proved that it can be received successfully")
|
||||
.startAt(System.currentTimeMillis())
|
||||
.activeAt(System.currentTimeMillis())
|
||||
.endAt(System.currentTimeMillis())
|
||||
.triggerTimes(2)
|
||||
.annotations(annotations)
|
||||
.status("firing")
|
||||
.build();
|
||||
SingleAlert singleAlert2 = SingleAlert.builder()
|
||||
.labels(labels)
|
||||
.content("test send msg! \\n This is the test data. It is proved that it can be received successfully")
|
||||
.startAt(System.currentTimeMillis())
|
||||
.activeAt(System.currentTimeMillis())
|
||||
.endAt(System.currentTimeMillis())
|
||||
.triggerTimes(4)
|
||||
.annotations(annotations)
|
||||
.status("firing")
|
||||
.build();
|
||||
GroupAlert groupAlert = GroupAlert.builder()
|
||||
.commonLabels(Map.of(CommonConstants.LABEL_ALERT_NAME, "CPU Usage Alert"))
|
||||
.commonAnnotations(annotations)
|
||||
.alerts(List.of(singleAlert1, singleAlert2))
|
||||
.status("firing")
|
||||
.build();
|
||||
return dispatcherAlarm.sendNoticeMsg(noticeReceiver, null, groupAlert);
|
||||
}
|
||||
|
||||
private void clearNoticeRulesCache() {
|
||||
CommonCacheService<String, Object> noticeCache = CacheFactory.getNoticeCache();
|
||||
noticeCache.remove(CommonConstants.CACHE_NOTICE_RULE);
|
||||
CacheFactory.clearNoticeCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dto.PrometheusExternAlert;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
|
||||
import org.apache.hertzbeat.alert.service.ExternAlertService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Prometheus external alarm service impl
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PrometheusExternAlertService implements ExternAlertService {
|
||||
|
||||
@Autowired
|
||||
private AlarmCommonReduce alarmCommonReduce;
|
||||
|
||||
|
||||
@Override
|
||||
public void addExternAlert(String content) {
|
||||
|
||||
TypeReference<List<PrometheusExternAlert>> typeReference = new TypeReference<>() {};
|
||||
List<PrometheusExternAlert> alerts = JsonUtil.fromJson(content, typeReference);
|
||||
if (alerts == null || alerts.isEmpty()) {
|
||||
log.warn("parse prometheus extern alert content failed! content: {}", content);
|
||||
return;
|
||||
}
|
||||
for (PrometheusExternAlert alert : alerts) {
|
||||
Map<String, String> annotations = alert.getAnnotations();
|
||||
if (annotations == null) {
|
||||
annotations = new HashMap<>(8);
|
||||
}
|
||||
if (StringUtils.hasText(alert.getGeneratorURL())) {
|
||||
annotations.put("generatorURL", alert.getGeneratorURL());
|
||||
}
|
||||
String description = annotations.get("description");
|
||||
if (description == null) {
|
||||
description = annotations.get("summary");
|
||||
}
|
||||
if (description == null) {
|
||||
description = annotations.values().stream().findFirst().orElse("");
|
||||
}
|
||||
Map<String, String> labels = alert.getLabels();
|
||||
if (labels == null) {
|
||||
labels = new HashMap<>(8);
|
||||
}
|
||||
labels.put("__source__", "prometheus");
|
||||
String status = CommonConstants.ALERT_STATUS_FIRING;
|
||||
if (alert.getEndsAt() != null && alert.getEndsAt().isBefore(Instant.now())) {
|
||||
status = CommonConstants.ALERT_STATUS_RESOLVED;
|
||||
}
|
||||
SingleAlert singleAlert = SingleAlert.builder()
|
||||
.content(description)
|
||||
.status(status)
|
||||
.activeAt(CommonConstants.ALERT_STATUS_FIRING.equals(status) ? Instant.now().toEpochMilli() : null)
|
||||
.startAt(alert.getStartsAt() != null ? alert.getStartsAt().toEpochMilli() : Instant.now().toEpochMilli())
|
||||
.endAt(CommonConstants.ALERT_STATUS_RESOLVED.equals(status) ? alert.getEndsAt().toEpochMilli() : null)
|
||||
.labels(labels)
|
||||
.annotations(alert.getAnnotations())
|
||||
.triggerTimes(1)
|
||||
.build();
|
||||
|
||||
alarmCommonReduce.reduceAndSendAlarm(singleAlert);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportSource() {
|
||||
return "prometheus";
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hertzbeat.alert.dto.TencentCloudExternAlert;
|
||||
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
|
||||
import org.apache.hertzbeat.alert.service.ExternAlertService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Default external alarm service impl
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TencentExternAlertService implements ExternAlertService {
|
||||
|
||||
@Autowired
|
||||
private AlarmCommonReduce alarmCommonReduce;
|
||||
|
||||
@Override
|
||||
public void addExternAlert(String content) {
|
||||
TencentCloudExternAlert report = JsonUtil.fromJson(content, TencentCloudExternAlert.class);
|
||||
if (report == null) {
|
||||
log.warn("parse extern alert content failed! content: {}", content);
|
||||
return;
|
||||
}
|
||||
// todo convert TenCloudAlertReport to SingleAlert
|
||||
SingleAlert alert = new SingleAlert();
|
||||
alarmCommonReduce.reduceAndSendAlarm(alert);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportSource() {
|
||||
return "tencent";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# 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.
|
||||
|
||||
alerter.availability.recover = 可用性警報已解決,監視狀態現在正常
|
||||
alerter.alarm.recover = 警報解決通知
|
||||
alerter.notify.title = HertzBeat 警報通知
|
||||
alerter.notify.target = 監視目標
|
||||
alerter.notify.monitorId = 監視 ID
|
||||
alerter.notify.monitorName = 監視名稱
|
||||
alerter.notify.monitorHost = 監視主機
|
||||
alerter.notify.priority = 警報優先級
|
||||
alerter.notify.triggerTime = 警報觸發時間
|
||||
alerter.notify.restoreTime = 警報恢復時間
|
||||
alerter.notify.times = 警報觸發次數
|
||||
alerter.notify.tags = 警報標籤
|
||||
alerter.notify.content = 警報內容
|
||||
alerter.notify.console = 控制台登錄
|
||||
alerter.priority.0 = 緊急警報
|
||||
alerter.priority.1 = 嚴重警報
|
||||
alerter.priority.2 = 警告警報
|
||||
+1
-32
@@ -64,12 +64,8 @@ class AlertDefineControllerTest {
|
||||
|
||||
this.alertDefine = AlertDefine.builder()
|
||||
.id(1L)
|
||||
.app("app")
|
||||
.metric("test")
|
||||
.field("test")
|
||||
.preset(false)
|
||||
.name("alertDefine")
|
||||
.expr("1 > 0")
|
||||
.priority((byte) 1)
|
||||
.times(1)
|
||||
.template("template")
|
||||
.creator("tom")
|
||||
@@ -125,9 +121,6 @@ class AlertDefineControllerTest {
|
||||
.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()))
|
||||
@@ -144,28 +137,4 @@ class AlertDefineControllerTest {
|
||||
.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();
|
||||
}
|
||||
}
|
||||
|
||||
+17
-24
@@ -19,10 +19,8 @@ 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;
|
||||
@@ -92,8 +90,7 @@ class AlertDefinesControllerTest {
|
||||
pageRequest = PageRequest.of((Integer) content.get("pageIndex"), (Integer) content.get("pageSize"), sortExp);
|
||||
}
|
||||
|
||||
// @Test
|
||||
// todo: fix this test
|
||||
@Test
|
||||
void getAlertDefines() throws Exception {
|
||||
|
||||
// Test the correctness of the mock
|
||||
@@ -110,32 +107,28 @@ class AlertDefinesControllerTest {
|
||||
// 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(null, null, null, "id", "desc", 1, 10)).thenReturn(new PageImpl<>(Collections.singletonList(define)));
|
||||
AlertDefine define = AlertDefine.builder().id(9L).expr("x").times(1).build();
|
||||
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, Sort.by(Sort.Order.asc(sort)));
|
||||
Mockito.when(alertDefineService.getAlertDefines(
|
||||
Mockito.eq(ids),
|
||||
Mockito.isNull(),
|
||||
Mockito.eq(sort),
|
||||
Mockito.eq(order),
|
||||
Mockito.eq(pageIndex),
|
||||
Mockito.eq(pageSize)
|
||||
)).thenReturn(new PageImpl<>(Collections.singletonList(define), pageRequest, 1));
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders.get(
|
||||
"/api/alert/defines")
|
||||
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()))
|
||||
.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").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))
|
||||
.andExpect(jsonPath("$.data.content[0].id").value(9))
|
||||
.andExpect(jsonPath("$.data.content[0].expr").value("x"))
|
||||
.andExpect(jsonPath("$.data.content[0].times").value(1))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
|
||||
+30
-31
@@ -27,9 +27,10 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
|
||||
import org.apache.hertzbeat.alert.service.AlertConvergeService;
|
||||
|
||||
import org.apache.hertzbeat.alert.service.AlertGroupConvergeService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertConverge;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -41,28 +42,27 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
/**
|
||||
* test case for {@link AlertConvergeController}
|
||||
* test case for {@link AlertGroupConvergeController}
|
||||
*/
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class AlertConvergeControllerTest {
|
||||
public class AlertGroupConvergeControllerTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Mock
|
||||
private AlertConvergeService alertConvergeService;
|
||||
private AlertGroupConvergeService alertGroupConvergeService;
|
||||
|
||||
private AlertConverge alertConverge;
|
||||
private AlertGroupConverge alertGroupConverge;
|
||||
|
||||
@InjectMocks
|
||||
private AlertConvergeController alertConvergeController;
|
||||
private AlertGroupConvergeController alertGroupConvergeController;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
this.mockMvc = standaloneSetup(alertConvergeController).build();
|
||||
this.mockMvc = standaloneSetup(alertGroupConvergeController).build();
|
||||
|
||||
alertConverge = AlertConverge.builder()
|
||||
alertGroupConverge = AlertGroupConverge.builder()
|
||||
.name("test")
|
||||
.creator("admin")
|
||||
.modifier("admin")
|
||||
@@ -71,54 +71,53 @@ public class AlertConvergeControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddNewAlertConverge() throws Exception {
|
||||
void testAddNewAlertGroupConverge() throws Exception {
|
||||
|
||||
doNothing().when(alertConvergeService).validate(any(AlertConverge.class), eq(false));
|
||||
doNothing().when(alertConvergeService).addAlertConverge(any(AlertConverge.class));
|
||||
doNothing().when(alertGroupConvergeService).validate(any(AlertGroupConverge.class), eq(false));
|
||||
doNothing().when(alertGroupConvergeService).addAlertGroupConverge(any(AlertGroupConverge.class));
|
||||
|
||||
mockMvc.perform(post("/api/alert/converge")
|
||||
mockMvc.perform(post("/api/alert/group")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(alertConverge))
|
||||
).andExpect(status().isOk())
|
||||
.content(JsonUtil.toJson(alertGroupConverge)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.msg").value("Add success"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testModifyAlertConverge() throws Exception {
|
||||
void testModifyAlertGroupConverge() throws Exception {
|
||||
|
||||
doNothing().when(alertConvergeService).validate(any(AlertConverge.class), eq(true));
|
||||
doNothing().when(alertConvergeService).modifyAlertConverge(any(AlertConverge.class));
|
||||
doNothing().when(alertGroupConvergeService).validate(any(AlertGroupConverge.class), eq(true));
|
||||
doNothing().when(alertGroupConvergeService).modifyAlertGroupConverge(any(AlertGroupConverge.class));
|
||||
|
||||
mockMvc.perform(put("/api/alert/converge")
|
||||
mockMvc.perform(put("/api/alert/group")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(alertConverge))
|
||||
).andExpect(status().isOk())
|
||||
.content(JsonUtil.toJson(alertGroupConverge)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.msg").value("Modify success"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAlertConvergeExists() throws Exception {
|
||||
void testGetAlertGroupConvergeExists() throws Exception {
|
||||
|
||||
when(alertConvergeService.getAlertConverge(1L)).thenReturn(alertConverge);
|
||||
when(alertGroupConvergeService.getAlertGroupConverge(1L)).thenReturn(alertGroupConverge);
|
||||
|
||||
mockMvc.perform(get("/api/alert/converge/{id}", 1L)
|
||||
mockMvc.perform(get("/api/alert/group/{id}", 1L)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.data.id").value(alertConverge.getId()));
|
||||
.andExpect(jsonPath("$.data.id").value(alertGroupConverge.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAlertConvergeNotExists() throws Exception {
|
||||
void testGetAlertGroupConvergeNotExists() throws Exception {
|
||||
|
||||
when(alertConvergeService.getAlertConverge(1L)).thenReturn(null);
|
||||
when(alertGroupConvergeService.getAlertGroupConverge(1L)).thenReturn(null);
|
||||
|
||||
mockMvc.perform(get("/api/alert/converge/{id}", 1L)
|
||||
mockMvc.perform(get("/api/alert/group/{id}", 1L)
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.MONITOR_NOT_EXIST_CODE))
|
||||
.andExpect(jsonPath("$.msg").value("AlertConverge not exist."));
|
||||
.andExpect(jsonPath("$.msg").value("Alert Group Converge not exist."));
|
||||
}
|
||||
|
||||
}
|
||||
+21
-21
@@ -29,9 +29,9 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standal
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.alert.service.AlertConvergeService;
|
||||
import org.apache.hertzbeat.alert.service.AlertGroupConvergeService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertConverge;
|
||||
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -47,58 +47,59 @@ import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
|
||||
/**
|
||||
* test case for {@link AlertConvergesController}
|
||||
* test case for {@link AlertGroupConvergesController}
|
||||
*/
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AlertConvergesControllerTest {
|
||||
class AlertGroupConvergesControllerTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Mock
|
||||
private AlertConvergeService alertConvergeService;
|
||||
private AlertGroupConvergeService alertGroupConvergeService;
|
||||
|
||||
@InjectMocks
|
||||
private AlertConvergesController alertConvergesController;
|
||||
private AlertGroupConvergesController alertGroupConvergesController;
|
||||
|
||||
private List<AlertConverge> alertConvergeList;
|
||||
private List<AlertGroupConverge> alertGroupConvergeList;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
this.mockMvc = standaloneSetup(alertConvergesController).build();
|
||||
this.mockMvc = standaloneSetup(alertGroupConvergesController).build();
|
||||
|
||||
AlertConverge alertConverge1 = AlertConverge.builder()
|
||||
AlertGroupConverge alertGroupConverge1 = AlertGroupConverge.builder()
|
||||
.name("Converge1")
|
||||
.id(1L)
|
||||
.build();
|
||||
|
||||
AlertConverge alertConverge2 = AlertConverge.builder()
|
||||
AlertGroupConverge alertGroupConverge2 = AlertGroupConverge.builder()
|
||||
.name("Converge2")
|
||||
.id(2L)
|
||||
.build();
|
||||
|
||||
alertConvergeList = Arrays.asList(alertConverge1, alertConverge2);
|
||||
alertGroupConvergeList = Arrays.asList(alertGroupConverge1, alertGroupConverge2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAlertConverges() throws Exception {
|
||||
void testGetAlertGroupConverges() throws Exception {
|
||||
|
||||
Page<AlertConverge> alertConvergePage = new PageImpl<>(
|
||||
alertConvergeList,
|
||||
Page<AlertGroupConverge> alertGroupConvergePage = new PageImpl<>(
|
||||
alertGroupConvergeList,
|
||||
PageRequest.of(0, 8, Sort.by("id").descending()),
|
||||
alertConvergeList.size()
|
||||
alertGroupConvergeList.size()
|
||||
);
|
||||
|
||||
when(alertConvergeService.getAlertConverges(null, null, "id", "desc", 0, 8)).thenReturn(alertConvergePage);
|
||||
when(alertGroupConvergeService.getAlertGroupConverges(null, null, "id", "desc", 0, 8)).thenReturn(alertGroupConvergePage);
|
||||
|
||||
mockMvc.perform(get("/api/alert/converges")
|
||||
mockMvc.perform(get("/api/alert/groups")
|
||||
.param("pageIndex", "0")
|
||||
.param("pageSize", "8")
|
||||
.param("sort", "id")
|
||||
.param("order", "desc")
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.data.content[0].id").value(1))
|
||||
.andExpect(jsonPath("$.data.content[0].name").value("Converge1"))
|
||||
.andExpect(jsonPath("$.data.content[1].id").value(2))
|
||||
@@ -108,13 +109,12 @@ class AlertConvergesControllerTest {
|
||||
@Test
|
||||
void testDeleteAlertDefines() throws Exception {
|
||||
|
||||
doNothing().when(alertConvergeService).deleteAlertConverges(eq(new HashSet<>(Arrays.asList(1L, 2L))));
|
||||
doNothing().when(alertGroupConvergeService).deleteAlertGroupConverges(eq(new HashSet<>(Arrays.asList(1L, 2L))));
|
||||
|
||||
mockMvc.perform(delete("/api/alert/converges")
|
||||
mockMvc.perform(delete("/api/alert/groups")
|
||||
.param("ids", "1,2")
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+32
-17
@@ -17,15 +17,19 @@
|
||||
|
||||
package org.apache.hertzbeat.alert.controller;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.when;
|
||||
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 java.util.HashMap;
|
||||
import org.apache.hertzbeat.alert.dto.TencentCloudExternAlert;
|
||||
import org.apache.hertzbeat.alert.service.ExternAlertService;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
@@ -36,16 +40,18 @@ 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 }
|
||||
*/
|
||||
@Disabled
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AlertReportControllerTest {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Mock
|
||||
private AlertService alertService;
|
||||
private ExternAlertService externAlertService;
|
||||
|
||||
@InjectMocks
|
||||
private AlertReportController alertReportController;
|
||||
@@ -57,17 +63,17 @@ class AlertReportControllerTest {
|
||||
|
||||
@Test
|
||||
void addNewAlertReportTencent() throws Exception {
|
||||
TenCloudAlertReport.Dimensions dimensions = new TenCloudAlertReport.Dimensions();
|
||||
TencentCloudExternAlert.Dimensions dimensions = new TencentCloudExternAlert.Dimensions();
|
||||
dimensions.setUnInstanceId("3333");
|
||||
|
||||
TenCloudAlertReport.AlarmObjInfo alarmObjInfo = new TenCloudAlertReport.AlarmObjInfo();
|
||||
TencentCloudExternAlert.AlarmObjInfo alarmObjInfo = new TencentCloudExternAlert.AlarmObjInfo();
|
||||
alarmObjInfo.setRegion("Guangzhou");
|
||||
alarmObjInfo.setNamespace("Guangzhou1");
|
||||
alarmObjInfo.setAppId("1111");
|
||||
alarmObjInfo.setUin("2222");
|
||||
alarmObjInfo.setDimensions(dimensions);
|
||||
|
||||
TenCloudAlertReport.Conditions conditions = new TenCloudAlertReport.Conditions();
|
||||
TencentCloudExternAlert.Conditions conditions = new TencentCloudExternAlert.Conditions();
|
||||
conditions.setMetricName("xx");
|
||||
conditions.setMetricShowName("xxx");
|
||||
conditions.setCalcType("a");
|
||||
@@ -80,12 +86,12 @@ class AlertReportControllerTest {
|
||||
conditions.setEventName("CVS");
|
||||
conditions.setEventShowName("Core error");
|
||||
|
||||
TenCloudAlertReport.AlarmPolicyInfo alarmPolicyInfo = new TenCloudAlertReport.AlarmPolicyInfo();
|
||||
TencentCloudExternAlert.AlarmPolicyInfo alarmPolicyInfo = new TencentCloudExternAlert.AlarmPolicyInfo();
|
||||
alarmPolicyInfo.setPolicyTypeCname("x");
|
||||
alarmPolicyInfo.setPolicyName("Test1");
|
||||
alarmPolicyInfo.setConditions(conditions);
|
||||
|
||||
TenCloudAlertReport report = TenCloudAlertReport.builder()
|
||||
TencentCloudExternAlert report = TencentCloudExternAlert.builder()
|
||||
.sessionId("123")
|
||||
.alarmStatus("1")
|
||||
.alarmType("event")
|
||||
@@ -94,31 +100,40 @@ class AlertReportControllerTest {
|
||||
.alarmObjInfo(alarmObjInfo)
|
||||
.alarmPolicyInfo(alarmPolicyInfo)
|
||||
.build();
|
||||
when(externAlertService.supportSource()).thenReturn("tencent");
|
||||
doNothing().when(externAlertService).addExternAlert(anyString());
|
||||
mockMvc.perform(
|
||||
MockMvcRequestBuilders
|
||||
.post("/api/alerts/report/tencloud")
|
||||
.post("/api/alerts/report/tencent")
|
||||
.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");
|
||||
|
||||
SingleAlert singleAlert = SingleAlert.builder()
|
||||
.fingerprint("fingerprint")
|
||||
.labels(new HashMap<>())
|
||||
.annotations(new HashMap<>())
|
||||
.content("content")
|
||||
.status("firing")
|
||||
.triggerTimes(1)
|
||||
.startAt(1734005477630L)
|
||||
.activeAt(1734005477630L)
|
||||
.build();
|
||||
when(externAlertService.supportSource()).thenReturn("default");
|
||||
doNothing().when(externAlertService).addExternAlert(anyString());
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.post("/api/alerts/report")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(JsonUtil.toJson(generalCloudAlertReport))
|
||||
.content(JsonUtil.toJson(singleAlert))
|
||||
)
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(content().json("{\"data\":null,\"msg\":\"Add report success\",\"code\":0}"))
|
||||
.andReturn();
|
||||
}
|
||||
}
|
||||
|
||||
+12
-29
@@ -27,7 +27,7 @@ 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.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -71,28 +71,23 @@ class AlertsControllerTest {
|
||||
void getAlerts() throws Exception {
|
||||
String sortField = "id";
|
||||
String orderType = "desc";
|
||||
Byte priority = 1;
|
||||
Byte status = 1;
|
||||
Long monitorId = 1L;
|
||||
String status = "firing";
|
||||
String content = "test";
|
||||
int pageIndex = 0;
|
||||
int pageSize = 10;
|
||||
|
||||
Page<Alert> alertPage = new PageImpl<>(
|
||||
Collections.singletonList(Alert.builder().build()),
|
||||
Page<GroupAlert> alertPage = new PageImpl<>(
|
||||
Collections.singletonList(GroupAlert.builder().build()),
|
||||
PageRequest.of(pageIndex, pageSize, Sort.by(sortField).descending()),
|
||||
ids.size()
|
||||
);
|
||||
Mockito.when(alertService.getAlerts(ids, monitorId, priority, status, content, sortField, orderType, pageIndex, pageSize))
|
||||
Mockito.when(alertService.getGroupAlerts(status, content, sortField, orderType, pageIndex, pageSize))
|
||||
.thenReturn(alertPage);
|
||||
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.get("/api/alerts")
|
||||
.param("ids", ids.stream().map(String::valueOf).collect(Collectors.joining(",")))
|
||||
.param("monitorId", String.valueOf(monitorId))
|
||||
.param("priority", String.valueOf(priority))
|
||||
.param("status", String.valueOf(status))
|
||||
.param("content", content)
|
||||
.get("/api/alerts/group")
|
||||
.param("status", status)
|
||||
.param("search", content)
|
||||
.param("sort", sortField)
|
||||
.param("order", orderType)
|
||||
.param("pageIndex", String.valueOf(pageIndex))
|
||||
@@ -105,10 +100,10 @@ class AlertsControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteAlerts() throws Exception {
|
||||
void deleteGroupAlerts() throws Exception {
|
||||
mockMvc.perform(
|
||||
MockMvcRequestBuilders
|
||||
.delete("/api/alerts")
|
||||
.delete("/api/alerts/group")
|
||||
.param("ids", ids.stream().map(String::valueOf).collect(Collectors.joining(",")))
|
||||
)
|
||||
.andExpect(status().isOk())
|
||||
@@ -118,22 +113,10 @@ class AlertsControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void clearAllAlerts() throws Exception {
|
||||
void applyGroupAlertStatus() 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")
|
||||
.put("/api/alerts/group/status/resolved")
|
||||
.param("ids", ids.stream().map(String::valueOf).collect(Collectors.joining(",")))
|
||||
)
|
||||
.andExpect(status().isOk())
|
||||
|
||||
+119
-38
@@ -15,9 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hertzbeat.manager.controller;
|
||||
package org.apache.hertzbeat.alert.controller;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -28,17 +27,15 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||
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.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.apache.hertzbeat.alert.service.impl.NoticeConfigServiceImpl;
|
||||
import org.apache.hertzbeat.common.constants.CommonConstants;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.manager.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.entity.manager.TagItem;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeRule;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
import org.apache.hertzbeat.common.util.JsonUtil;
|
||||
import org.apache.hertzbeat.manager.service.impl.NoticeConfigServiceImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -46,6 +43,10 @@ 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.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
@@ -67,12 +68,6 @@ class NoticeConfigControllerTest {
|
||||
|
||||
|
||||
public NoticeRule getNoticeRule() {
|
||||
List<TagItem> tags = new ArrayList<>();
|
||||
TagItem tagItem = new TagItem();
|
||||
tagItem.setName("key1");
|
||||
tagItem.setValue("value1");
|
||||
tags.add(tagItem);
|
||||
|
||||
NoticeRule noticeRule = new NoticeRule();
|
||||
noticeRule.setId(87584674384L);
|
||||
noticeRule.setName("dispatch-1");
|
||||
@@ -82,7 +77,6 @@ class NoticeConfigControllerTest {
|
||||
noticeRule.setTemplateName("test");
|
||||
noticeRule.setCreator("tom");
|
||||
noticeRule.setModifier("tom");
|
||||
noticeRule.setTags(tags);
|
||||
|
||||
return noticeRule;
|
||||
}
|
||||
@@ -177,12 +171,38 @@ class NoticeConfigControllerTest {
|
||||
|
||||
@Test
|
||||
void getReceivers() throws Exception {
|
||||
NoticeReceiver receiver1 = new NoticeReceiver();
|
||||
receiver1.setId(1L);
|
||||
receiver1.setName("Receiver1");
|
||||
|
||||
//Mockito.when(noticeConfigService.getNoticeReceivers())
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/receivers?name={name}", "tom"))
|
||||
NoticeReceiver receiver2 = new NoticeReceiver();
|
||||
receiver2.setId(2L);
|
||||
receiver2.setName("Receiver2");
|
||||
|
||||
Page<NoticeReceiver> receiverPage = new PageImpl<>(
|
||||
Arrays.asList(receiver1, receiver2),
|
||||
PageRequest.of(0, 8, Sort.by("id").descending()),
|
||||
2
|
||||
);
|
||||
|
||||
when(noticeConfigService.getNoticeReceivers("Receiver", 0, 8)).thenReturn(receiverPage);
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/receivers")
|
||||
.param("name", "Receiver")
|
||||
.param("pageIndex", "0")
|
||||
.param("pageSize", "8")
|
||||
.param("sort", "id")
|
||||
.param("order", "desc")
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
.andExpect(jsonPath("$.data.content[0].id").value(1))
|
||||
.andExpect(jsonPath("$.data.content[0].name").value("Receiver1"))
|
||||
.andExpect(jsonPath("$.data.content[1].id").value(2))
|
||||
.andExpect(jsonPath("$.data.content[1].name").value("Receiver2"))
|
||||
.andExpect(jsonPath("$.data.totalElements").value(2))
|
||||
.andExpect(jsonPath("$.data.totalPages").value(1))
|
||||
.andExpect(jsonPath("$.data.size").value(8))
|
||||
.andExpect(jsonPath("$.data.number").value(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -254,15 +274,38 @@ class NoticeConfigControllerTest {
|
||||
|
||||
@Test
|
||||
void getRules() throws Exception {
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/rules"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
NoticeRule rule1 = new NoticeRule();
|
||||
rule1.setId(1L);
|
||||
rule1.setName("Rule1");
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/rules?name={name}", "tom"))
|
||||
NoticeRule rule2 = new NoticeRule();
|
||||
rule2.setId(2L);
|
||||
rule2.setName("Rule2");
|
||||
|
||||
Page<NoticeRule> rulePage = new PageImpl<>(
|
||||
Arrays.asList(rule1, rule2),
|
||||
PageRequest.of(0, 8, Sort.by("id").descending()),
|
||||
2
|
||||
);
|
||||
|
||||
when(noticeConfigService.getNoticeRules("Rule", 0, 8)).thenReturn(rulePage);
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/rules")
|
||||
.param("name", "Rule")
|
||||
.param("pageIndex", "0")
|
||||
.param("pageSize", "8")
|
||||
.param("sort", "id")
|
||||
.param("order", "desc")
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
.andExpect(jsonPath("$.data.content[0].id").value(1))
|
||||
.andExpect(jsonPath("$.data.content[0].name").value("Rule1"))
|
||||
.andExpect(jsonPath("$.data.content[1].id").value(2))
|
||||
.andExpect(jsonPath("$.data.content[1].name").value("Rule2"))
|
||||
.andExpect(jsonPath("$.data.totalElements").value(2))
|
||||
.andExpect(jsonPath("$.data.totalPages").value(1))
|
||||
.andExpect(jsonPath("$.data.size").value(8))
|
||||
.andExpect(jsonPath("$.data.number").value(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -371,22 +414,40 @@ class NoticeConfigControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetTemplates() throws Exception {
|
||||
// Mock the service response
|
||||
void getTemplates() throws Exception {
|
||||
NoticeTemplate template1 = new NoticeTemplate();
|
||||
template1.setId(1L);
|
||||
template1.setName("Template1");
|
||||
NoticeTemplate template2 = new NoticeTemplate();
|
||||
template2.setName("Template2");
|
||||
List<NoticeTemplate> templates = Arrays.asList(template1, template2);
|
||||
when(noticeConfigService.getNoticeTemplates(any())).thenReturn(templates);
|
||||
|
||||
// Perform the GET request and verify the response
|
||||
this.mockMvc.perform(get("/api/notice/templates")
|
||||
.param("name", "Template"))
|
||||
NoticeTemplate template2 = new NoticeTemplate();
|
||||
template2.setId(2L);
|
||||
template2.setName("Template2");
|
||||
|
||||
Page<NoticeTemplate> templatePage = new PageImpl<>(
|
||||
Arrays.asList(template1, template2),
|
||||
PageRequest.of(0, 8, Sort.by("id").descending()),
|
||||
2
|
||||
);
|
||||
|
||||
when(noticeConfigService.getNoticeTemplates("Template", true, 0, 8)).thenReturn(templatePage);
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/templates")
|
||||
.param("name", "Template")
|
||||
.param("preset", "true")
|
||||
.param("pageIndex", "0")
|
||||
.param("pageSize", "8")
|
||||
.param("sort", "id")
|
||||
.param("order", "desc")
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andExpect(jsonPath("$.data[0].name").value("Template1"))
|
||||
.andExpect(jsonPath("$.data[1].name").value("Template2"));
|
||||
.andExpect(jsonPath("$.data.content[0].id").value(1))
|
||||
.andExpect(jsonPath("$.data.content[0].name").value("Template1"))
|
||||
.andExpect(jsonPath("$.data.content[1].id").value(2))
|
||||
.andExpect(jsonPath("$.data.content[1].name").value("Template2"))
|
||||
.andExpect(jsonPath("$.data.totalElements").value(2))
|
||||
.andExpect(jsonPath("$.data.totalPages").value(1))
|
||||
.andExpect(jsonPath("$.data.size").value(8))
|
||||
.andExpect(jsonPath("$.data.number").value(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -423,5 +484,25 @@ class NoticeConfigControllerTest {
|
||||
verify(noticeConfigService, times(1)).sendTestMsg(noticeReceiver);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllTemplates() throws Exception {
|
||||
List<NoticeTemplate> templates = Arrays.asList(new NoticeTemplate(), new NoticeTemplate());
|
||||
when(noticeConfigService.getAllNoticeTemplates()).thenReturn(templates);
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/templates/all"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllReceivers() throws Exception {
|
||||
List<NoticeReceiver> receivers = Arrays.asList(new NoticeReceiver(), new NoticeReceiver());
|
||||
when(noticeConfigService.getAllNoticeReceivers()).thenReturn(receivers);
|
||||
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/notice/receivers/all"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value((int) CommonConstants.SUCCESS_CODE))
|
||||
.andReturn();
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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.notice;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.apache.hertzbeat.alert.AlerterWorkerPool;
|
||||
import org.apache.hertzbeat.alert.config.AlertSseManager;
|
||||
import org.apache.hertzbeat.alert.service.NoticeConfigService;
|
||||
import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
|
||||
import org.apache.hertzbeat.plugin.runner.PluginRunner;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
|
||||
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
|
||||
|
||||
/**
|
||||
* Test case for Alert Notice Dispatch
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AlertNoticeDispatchTest {
|
||||
|
||||
@Mock
|
||||
private AlerterWorkerPool workerPool;
|
||||
|
||||
@Mock
|
||||
private NoticeConfigService noticeConfigService;
|
||||
|
||||
@Mock
|
||||
private AlertStoreHandler alertStoreHandler;
|
||||
|
||||
@Mock
|
||||
private PluginRunner pluginRunner;
|
||||
|
||||
@Mock
|
||||
private AlertNotifyHandler alertNotifyHandler;
|
||||
|
||||
@Mock
|
||||
private AlertSseManager emitterManager;
|
||||
|
||||
private AlertNoticeDispatch alertNoticeDispatch;
|
||||
|
||||
private static final int DISPATCH_THREADS = 3;
|
||||
|
||||
private NoticeReceiver receiver;
|
||||
private GroupAlert alert;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
when(alertNotifyHandler.type()).thenReturn((byte) 1);
|
||||
|
||||
List<AlertNotifyHandler> alertNotifyHandlerList = List.of(alertNotifyHandler);
|
||||
alertNoticeDispatch = new AlertNoticeDispatch(
|
||||
workerPool,
|
||||
noticeConfigService,
|
||||
alertStoreHandler,
|
||||
alertNotifyHandlerList,
|
||||
pluginRunner,
|
||||
emitterManager
|
||||
);
|
||||
|
||||
receiver = NoticeReceiver.builder()
|
||||
.id(1L)
|
||||
.name("test-receiver")
|
||||
.type((byte) 1)
|
||||
.build();
|
||||
|
||||
alert = GroupAlert.builder()
|
||||
.id(1L)
|
||||
.status("firing")
|
||||
.alerts(Collections.singletonList(SingleAlert.builder()
|
||||
.content("test-content")
|
||||
.build()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSendNoticeMsg() {
|
||||
NoticeTemplate template = new NoticeTemplate();
|
||||
template.setId(1L);
|
||||
template.setName("default-template");
|
||||
when(noticeConfigService.getDefaultNoticeTemplateByType((byte) 1)).thenReturn(template);
|
||||
doNothing().when(alertNotifyHandler).send(eq(receiver), eq(template), eq(alert));
|
||||
|
||||
assertTrue(alertNoticeDispatch.sendNoticeMsg(receiver, null, alert));
|
||||
verify(alertNotifyHandler).send(eq(receiver), eq(template), eq(alert));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSendNoticeMsgReceiverNull() {
|
||||
GroupAlert alert = new GroupAlert();
|
||||
alert.setId(1L);
|
||||
alert.setStatus("firing");
|
||||
|
||||
boolean result = alertNoticeDispatch.sendNoticeMsg(null, null, alert);
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSendNoticeMsgReceiverTypeNull() {
|
||||
NoticeReceiver receiver = new NoticeReceiver();
|
||||
receiver.setId(1L);
|
||||
receiver.setName("test-receiver");
|
||||
|
||||
GroupAlert alert = new GroupAlert();
|
||||
alert.setId(1L);
|
||||
alert.setStatus("firing");
|
||||
|
||||
boolean result = alertNoticeDispatch.sendNoticeMsg(receiver, null, alert);
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSendNoticeMsgNoHandler() {
|
||||
NoticeReceiver receiver = new NoticeReceiver();
|
||||
receiver.setId(1L);
|
||||
receiver.setName("test-receiver");
|
||||
receiver.setType((byte) 2);
|
||||
|
||||
GroupAlert alert = new GroupAlert();
|
||||
alert.setId(1L);
|
||||
alert.setStatus("firing");
|
||||
|
||||
assertFalse(alertNoticeDispatch.sendNoticeMsg(receiver, null, alert));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user