Compare commits

...
116 Commits
Author SHA1 Message Date
Logic 821c62ba60 Merge branch 'master' into copy-token 2025-01-18 20:55:10 +08:00
wanhaoandaias00 aed524a88b [feature] Added multilingual defaults for forms (#2991)
Co-authored-by: aias00 <rokkki@163.com>
2025-01-18 20:54:57 +08:00
Logic fbc1c0a29a Merge branch 'master' into copy-token 2025-01-17 23:25:58 +08:00
aias00 9b20ab8434 [type:fix] remove matomo ip (#2990) 2025-01-17 23:15:44 +08:00
Logic 648064227f refactor(web-app): update alert integration modal button layout
- Swap OK and Cancel button positions in alert integration modal- Improve user experience by aligning with common UI practices
2025-01-17 23:05:53 +08:00
Logic a08fdd210c feat(alert): add copy button for alert integration token
- Add a copy button below the token display
- Implement copyToken() method to copy the token to clipboard
- Show a success notification after copying
2025-01-17 22:38:49 +08:00
5c39b98eaa [feature] Add privateKey passphrase config for linux monitor (#2982)
Co-authored-by: yinyijun <yinyijun6@mgtv.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: aias00 <rokkki@163.com>
2025-01-17 22:05:14 +08:00
aias00 43ac859939 [type:improve] fix dependencies vulnerabilites (#2989) 2025-01-17 20:31:57 +08:00
helei1030andtomsun28 b5c2eca819 [docs]added spark Chinese documents and changed the original spar… (#2987)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-17 10:41:19 +08:00
PJ Fanning cd57f51892 [MINOR UPDATE] improve xml parsing code (#2988)
Signed-off-by: PJ Fanning <pjfanning@users.noreply.github.com>
2025-01-17 09:17:57 +08:00
tomsun28 a33966de9f [improve] update i18n json stru and update search ui (#2986) 2025-01-16 20:46:32 +08:00
4ff9452926 [bugfix] fix hbase dashboard display anomalies and turn on HTTPS (#2980)
Co-authored-by: zhuoyuchen <>
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: Jast <shenghang@apache.org>
2025-01-15 18:20:21 +08:00
Jast 67da9bdbba [Feature] Support copy monitoring (#2981) 2025-01-15 17:03:53 +08:00
tomsun28 8bb4493b43 [bugfix] fix alert integration extern source bug (#2979) 2025-01-14 18:22:21 +08:00
ghyandshown 92edd53f8c [webapp] key-value-input component hover effect fixed (#2972)
Signed-off-by: ghy <ghyghoo8@qq.com>
Co-authored-by: shown <yuluo08290126@gmail.com>
2025-01-13 15:44:14 +08:00
d70ad75dcc [bugfix] Fix NullPointerException (#2849)
Signed-off-by: Ayu <127600988+ayu-v0@users.noreply.github.com>
Signed-off-by: Zhang Yuxuan <1373529784@qq.com>
Co-authored-by: Ayu <14359932+lin-Ayu0v0@user.noreply.gitee.com>
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: Zhang Yuxuan <1373529784@qq.com>
2025-01-12 22:52:37 +08:00
tomsun28andLogic 2e76c12fe1 [feature] alert integration extern source (#2978)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: Logic <zqr10159@dromara.org>
2025-01-12 16:13:52 +08:00
Nick Guoandtomsun28 7e158eb778 [Improve] Add more test cases for Kafka junit tests (#2976)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-11 17:05:04 +08:00
Logicandtomsun28 db23c94882 [bugfix](warehouse) add metrics data update logic in memory storage (#2973)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-10 20:10:01 +08:00
yunfan24andaias00 e905f36de2 [bugfix] Fix the search functionality issue. (#2970)
Co-authored-by: aias00 <rokkki@163.com>
2025-01-10 19:36:22 +08:00
aias00andshown 037957dbe6 [feature] replace googletagmanager to matomo (#2877)
Co-authored-by: shown <yuluo08290126@gmail.com>
2025-01-09 16:28:17 +08:00
liutianyouandtomsun28 01d18f7935 [Improve] add no popup option after next login (#2969)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-08 23:47:47 +08:00
88e350de1c [BUG] "Advanced Settings" is all white in dark mode (#2965)
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-08 23:43:27 +08:00
Nick Guoandtomsun28 07d22a8712 [Improve] Standardize Kafka metric naming (#2961)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-08 23:38:29 +08:00
tomsun28 7847fd78b7 [bufix] fix collector job scheduler error (#2966)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-01-08 21:44:42 +08:00
tomsun28 f7676ac8ca [improve] update alarm inhibit rule and alarm ui (#2957) 2025-01-08 10:04:07 +08:00
Jastandtomsun28 dd1cc700d9 [Imporve] Improve Huaweicloud (#2954)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-07 23:05:05 +08:00
Nick Guo d8dabe251a [Doc]Add English version of documentation for Kafka Consumer Detail (#2953) 2025-01-07 10:12:47 +08:00
Nick GuoandLogic f2e647d777 【Improve】adjust log level from INFO to WARN. (#2952)
Co-authored-by: Logic <zqr10159@dromara.org>
2025-01-06 13:50:09 +08:00
tomsun28 7bac0245a2 [improve] fix some alarm relate bug, update alarm center ui (#2951)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2025-01-06 11:25:52 +08:00
Jast d6e5399121 [Improve]Modify Chinese comments (#2950) 2025-01-06 00:36:29 +08:00
doveLinandaias00 00076fc704 [Feature] Support customized JMX monitoring through the Factory Pattern. (#2932)
Co-authored-by: aias00 <rokkki@163.com>
2025-01-05 17:59:42 +08:00
yunfan24andtomsun28 4a3d61bd9e [doc] Add Supported MySQL Versions. (#2949)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-05 16:53:50 +08:00
Logic af59b487e4 [bugfix](db): optimize column update. (#2947) 2025-01-05 16:48:56 +08:00
4cd3c3c97f [alarm] refactor new alarm (#2902)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Calvin <naruse_shinji@163.com>
Co-authored-by: a-little-fool <2030509072@qq.com>
Co-authored-by: Logic <zqr10159@dromara.org>
2025-01-04 22:18:27 +08:00
69a21ffaa1 [refactor] refactoring methods replaceCryPlaceholder and replaceSmilingPlace (#2832)
Co-authored-by: Zhang Yuxuan <1373529784@qq.com>
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: Calvin <naruse_shinji@163.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-04 19:54:05 +08:00
Jast 8ab71e672f [Improve]Improve e2e code (#2945) 2025-01-04 15:58:22 +08:00
7625a0b508 [home] updated navbar css #2928 (#2934)
Signed-off-by: Vedant7789 <147625492+Vedant7789@users.noreply.github.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: aias00 <rokkki@163.com>
2025-01-03 17:43:51 +08:00
dc963833e8 [improve] Refactor and Split the Message Notification Component. (#2924)
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: aias00 <rokkki@163.com>
2025-01-03 17:15:56 +08:00
Craaaaazy77andshown 87d4379efb [bugfix] Modify the doris_be.md document into an English version (#2936)
Signed-off-by: Craaaaazy77 <417989286@qq.com>
Co-authored-by: shown <yuluo08290126@gmail.com>
2025-01-03 14:40:42 +08:00
Simon Sigréandtomsun28 bffd7c1d4f [bugfix] Corrected case 'DashBoard' is a lower case 'B' (#2935)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-02 20:27:24 +08:00
yunfan24 ba21ef5cdb [improve] Improve the synchronization of the mute status. (#2927) 2025-01-02 18:59:44 +08:00
Simon Sigréandtomsun28 3bf237a4eb [feature] Adding CPU Temperature Check Into Default Ubuntu Checks (#2930)
Signed-off-by: Simon Sigré <simon.sigre@gmail.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-02 00:57:05 +08:00
ponfeeandtomsun28 19e7f128a3 [improve] optimize website navbar css(#2928) (#2929)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-01 14:49:57 +08:00
Jastandtomsun28 5c409d7512 [Improve] Optimize the e2e code structure (#2926)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-01 14:14:26 +08:00
6bf20ba21a [bugfix] Fixed the 'java.lang.UnsupportedOperationException' exception caused by getCurrentMetricsData (#2923)
Co-authored-by: bcxc <lixiaobaivvv@gmail.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2025-01-01 14:00:32 +08:00
Simon Sigré 8c9b1f3f92 [doc] Update app-windows_script.yml (#2922)
Signed-off-by: Simon Sigré <simon.sigre@gmail.com>
2025-01-01 00:28:34 +08:00
yunfan24 b3eb0083df [bugfix] Bug fix for alarm voice. (#2920) 2024-12-30 23:41:48 +08:00
LogicandJast e51accb3e6 [improve](warehouse): replace empty json object key with empty string (#2919)
Co-authored-by: Jast <shenghang@apache.org>
2024-12-30 20:08:33 +08:00
Logic a9e219811a [bugfix]: fix setColumns method in CollectRep class (#2918) 2024-12-30 19:58:00 +08:00
Jastandtomsun28 51bdb56592 [Feture]Add docker e2e test (#2916)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-12-30 13:05:43 +08:00
yunfan24andtomsun28 3562e870d8 [improve] Add required field indicators and form validation prompts for convergence strategies and silent strategies in the form. (#2913)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-12-30 13:04:03 +08:00
6f9ee2f4e6 [improve] Optimize CacheService and add relevant unit test (#2912)
Co-authored-by: LLWW04 <1713673102@qq.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-12-29 23:52:36 +08:00
Jastandtomsun28 917c11f298 [bugfix] Fix docker container name unable to display problem (#2914)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-12-29 11:45:51 +08:00
bf8d5b79bb [bugfix] Fix the bug where canceling an edit on a record still updates the page values. (#2911)
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-12-29 11:38:57 +08:00
7178c444f2 [feature](web-app)Add Alarm Voice Alerts (#2906)
Signed-off-by: Logic <zqr10159@126.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: aias00 <rokkki@163.com>
2024-12-29 11:25:48 +08:00
Calvinandtomsun28 bc2dc1d567 [improve] fix import of CollectRep (#2910)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-12-28 14:04:11 +08:00
Jastandtomsun28 9714123c98 [Improve] Improve OBS (#2909)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-12-28 12:24:58 +08:00
leo 8ca2f70153 add an online parser for prometheus. (#2851) 2024-12-27 23:18:56 +08:00
doveLin 126268f7ec [feature] Add a new Singleton-pattern-based LRU local cache (#2907) 2024-12-27 14:39:00 +08:00
Logic 12d1f4990d [fix](flink): update calculate metrics definitions (#2905) 2024-12-26 23:45:02 +08:00
Jastandtomsun28 4b21df556c [Imporve] Support Kafka internal topic configuration (#2901)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-12-26 00:06:12 +08:00
Jast 893d91c4f0 [doc] update doc (#2900)
Signed-off-by: Jast <shenghang@apache.org>
2024-12-26 00:00:34 +08:00
4236c438be [feature] integrate with Apache Arrow (#2864)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Signed-off-by: shown <yuluo08290126@gmail.com>
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: aias00 <rokkki@163.com>
2024-12-24 20:43:14 +08:00
yunfan 6a64752af0 [Improve] add notification when port number changes automatically due to HTTPS toggle.(#2779) (#2896) 2024-12-24 18:22:26 +08:00
东风 c5cce27cb4 [improve]add Plc protocol , Modbus monitor (#2850) 2024-12-23 23:52:40 +08:00
doveLin 364de609bf [collector] fix the issue of reusing the adminClient in the Kafka client. (#2895) 2024-12-23 11:44:45 +08:00
Jast 5d81c32f9d [Improve] Improve Kafka chart display (#2894) 2024-12-23 10:53:53 +08:00
doveLinandtomsun28 6aff6ed053 [collector]add monitoring metrics for consumer groups in Kafka client (#2887)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-12-20 14:20:05 +08:00
Nick Guoandtomsun28 5ba09cf5c1 [doc] add more hints when users are switching data source. (#2880)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-12-20 13:52:46 +08:00
Logic a746e35dcf [Improve]Beautify Charts (#2891) 2024-12-20 12:06:28 +08:00
liutianyou a27777adcb [improve]improve linux process (#2889) 2024-12-20 07:41:17 +08:00
Logic 4b7dfb4c8e [improve] remove stack property from line charts (#2888) 2024-12-19 22:51:42 +08:00
AyuandAyu ceda4a65f9 [chore] delete the redundant else (#2881)
Co-authored-by: Ayu <14359932+lin-Ayu0v0@user.noreply.gitee.com>
2024-12-19 16:07:57 +08:00
d4f6fc1ae8 [Fix] fix clickhouse monitor (#2874)
Co-authored-by: Calvin <naruse_shinji@163.com>
Co-authored-by: aias00 <rokkki@163.com>
2024-12-17 14:20:27 +08:00
铁甲小宝 e999360da8 modified:add a small change. (#2878) 2024-12-16 00:16:29 +08:00
Nick Guo 948fcb739a [improve]Add more helpful messages when adding a Kafka monitor (#2876) 2024-12-14 15:22:27 +08:00
cdf060e9ac [Optimize] Add a reminder about potential collection issues caused by the Docker deployment method of collector. (#2844)
Co-authored-by: Calvin <naruse_shinji@163.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: aias00 <rokkki@163.com>
2024-12-12 20:51:02 +08:00
starryCoder 1280c61af1 [doc] improve windows monitoring doc(#2869) 2024-12-12 15:10:18 +08:00
Nick Guo 86ad10ad7b [doc] Add ',' separator between monitoring types (#2865) 2024-12-10 21:08:55 +08:00
8961cf70a3 [bugfix]A bug fix (#2853)
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: aias00 <rokkki@163.com>
2024-12-08 11:48:18 +08:00
tomsun28andaias00 f31909a30d [doc] update security doc and some (#2856)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: aias00 <rokkki@163.com>
2024-12-05 08:49:18 +08:00
Jastandtomsun28 2df7e1d269 [improve] improve dependency (#2855)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-12-04 21:24:37 +08:00
tomsun28andCalvin c10ebf0b60 [doc] add security model doc and update contributors (#2846)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: Calvin <naruse_shinji@163.com>
2024-12-04 13:58:16 +08:00
1fe70a60cc [bugfix]Fix wrong app name (#2845)
Co-authored-by: Calvin <naruse_shinji@163.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-11-30 16:43:09 +08:00
Jastandtomsun28 3ea4c0e7da [Feature] Add ssh e2e code (#2843)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-11-30 16:39:03 +08:00
tomsun28 8228938bd1 [fix] auto generated by protocol buffer (#2842)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2024-11-30 13:56:05 +08:00
5acaf4844a [feature] support managing tasks by using http_sd (#2830)
Co-authored-by: Zhang Yuxuan <1373529784@qq.com>
Co-authored-by: aias00 <rokkki@163.com>
2024-11-28 00:07:38 +08:00
fc8be06ad5 [improve] update victoriametrics and greptime store (#2836)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Signed-off-by: Logic <zqr10159@126.com>
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: Logic <zqr10159@dromara.org>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Zhang Yuxuan <1373529784@qq.com>
2024-11-27 08:58:09 +08:00
tomsun28 509a9c9651 [fix] bugfix flyway location can not auto detect vendor when not h2 (#2835) 2024-11-26 19:28:39 +08:00
Logic 754b18c9ba [doc] update sidebar category label and plugin documentation (#2837) 2024-11-26 14:36:40 +08:00
8cdc92ab56 [chore] Delete redundant Spaces (#2834)
Signed-off-by: ayu-v0 <127600988+ayu-v0@users.noreply.github.com>
Co-authored-by: Ayu <14359932+lin-Ayu0v0@user.noreply.gitee.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-11-24 21:01:25 +08:00
tomsun28andLogic f0cb2af031 [improve] update and fix template yml priority (#2829)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: Logic <zqr10159@dromara.org>
2024-11-24 15:38:21 +08:00
Jastandtomsun28 bd937bf7b8 [doc] Added custom development collector documentation (#2833)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-11-24 14:10:33 +08:00
starryCoder 8852713e4d [fix]fix windows chinese encoding (#2831) 2024-11-24 11:39:50 +08:00
CalvinandZhang Yuxuan 12f4ecce01 [refactor] change name from http_sd to registry (#2827)
Co-authored-by: Zhang Yuxuan <1373529784@qq.com>
2024-11-22 19:34:42 +08:00
Kerwin Bryant ca9934ec81 Fixed the omissions in #2805 (#2826)
Signed-off-by: Kerwin Bryant <kerwin612@qq.com>
2024-11-22 08:51:00 +08:00
tomsun28andshown eaaf589083 [improve] update grafana auth method and add expose url (#2818)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: shown <yuluo08290126@gmail.com>
2024-11-18 23:19:57 +08:00
tomsun28 be84a0abae [improve] update windows metrics yml (#2816)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2024-11-18 21:57:50 +08:00
Jast 9a00f5a776 [improve] modify e2e test (#2814) 2024-11-14 23:38:43 +08:00
fb4626dd07 [feature] add e2e code (#2811)
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-11-14 11:24:49 +08:00
Jast f28996255a [Improve] improve kafka monitor (#2813) 2024-11-13 21:35:04 +08:00
Jast 0b79222bd0 [Doc] Add blog (#2812) 2024-11-11 23:19:15 +08:00
liutianyouandZhang Yuxuan 120f3a12fe [fix] fix the Linux process monitoring process exits abnormally without warning (#2810)
Co-authored-by: Zhang Yuxuan <1373529784@qq.com>
2024-11-11 14:09:30 +08:00
b8b1885657 [improve] Fix error links caused by module name changes. (#2807)
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-11-09 22:17:51 +08:00
caf13c09bd [feature] Support custom refresh intervals for each group of metrics (#2718)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: crossoverJie <crossoverJie@gmail.com>
2024-11-09 13:41:31 +08:00
dca3e351ed [improve] code according to code specifications (#2809)
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: aias00 <rokkki@163.com>
2024-11-08 19:16:19 +08:00
Jast 6298e7ae90 [Improve] Improve module name (#2805) 2024-11-08 09:25:28 +08:00
tomsun28 4b2c3756b1 [doc] update new contributors (#2802)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
2024-11-04 11:12:16 +08:00
shinestare d32ec249b4 [fix]Remove the duplicate declaration of commons-net (#2801) 2024-11-03 11:03:01 +08:00
tomsun28andLogic 611ddc22e3 [bugfix] fix collector docker build error (#2799)
Signed-off-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: Logic <zqr10159@dromara.org>
2024-11-01 17:28:27 +08:00
4373ab23f3 [improve] improve the way Ai is entered and requested (#2762)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: Zhang Yuxuan <1373529784@qq.com>
2024-10-31 22:02:02 +08:00
bff75757db [Task][OSPP] HertzBeat Official Template Marketplace (#2641)
Co-authored-by: Calvin <naruse_shinji@163.com>
Co-authored-by: aias00 <rokkki@163.com>
Co-authored-by: shown <yuluo08290126@gmail.com>
Co-authored-by: tomsun28 <tomsun28@outlook.com>
Co-authored-by: Logic <zqr10159@dromara.org>
2024-10-30 20:07:49 +08:00
tomsun28 50dd262fbf [doc] update deploy doc (#2796) 2024-10-30 15:38:34 +08:00
Jastandtomsun28 dc9743b7a5 [Doc] improve website (#2795)
Co-authored-by: tomsun28 <tomsun28@outlook.com>
2024-10-30 11:02:16 +08:00
Logic 8dc0fdee27 [doc](download): update for v1.6.1 release (#2794) 2024-10-30 10:08:20 +08:00
1658 changed files with 95094 additions and 25592 deletions
+144
View File
@@ -2081,6 +2081,150 @@
"contributions": [
"doc"
]
},
{
"login": "shinestare",
"name": "shinestare",
"avatar_url": "https://avatars.githubusercontent.com/u/13570619?v=4",
"profile": "https://github.com/shinestare",
"contributions": [
"code"
]
},
{
"login": "po-168",
"name": "po-168",
"avatar_url": "https://avatars.githubusercontent.com/u/185745593?v=4",
"profile": "https://github.com/po-168",
"contributions": [
"code"
]
},
{
"login": "All-The-Best-for",
"name": "wbs99",
"avatar_url": "https://avatars.githubusercontent.com/u/76414672?v=4",
"profile": "https://github.com/All-The-Best-for",
"contributions": [
"code"
]
},
{
"login": "starryCoder",
"name": "starryCoder",
"avatar_url": "https://avatars.githubusercontent.com/u/46510059?v=4",
"profile": "https://github.com/starryCoder",
"contributions": [
"code"
]
},
{
"login": "hasimmollah",
"name": "hasimmollah",
"avatar_url": "https://avatars.githubusercontent.com/u/32538599?v=4",
"profile": "https://github.com/hasimmollah",
"contributions": [
"code"
]
},
{
"login": "ayu-v0",
"name": "Ayu",
"avatar_url": "https://avatars.githubusercontent.com/u/127600988?v=4",
"profile": "https://github.com/ayu-v0",
"contributions": [
"code"
]
},
{
"login": "Rancho-7",
"name": "Nick Guo",
"avatar_url": "https://avatars.githubusercontent.com/u/59016860?v=4",
"profile": "https://github.com/Rancho-7",
"contributions": [
"doc"
]
},
{
"login": "doveLin0818",
"name": "doveLin",
"avatar_url": "https://avatars.githubusercontent.com/u/190927907?v=4",
"profile": "https://github.com/doveLin0818",
"contributions": [
"code"
]
},
{
"login": "yunfan24",
"name": "yunfan24",
"avatar_url": "https://avatars.githubusercontent.com/u/91836599?v=4",
"profile": "https://zzrl.cc/",
"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"
]
}
],
"contributorsPerLine": 7,
+1 -1
View File
@@ -53,4 +53,4 @@ body:
validations:
required: false
- type: markdown
value: "Please read the [Contribution Guideline](https://hertzbeat.apache.org/docs/others/contributing) before submitting the PR"
value: "Please read the [Contribution Guideline](https://hertzbeat.apache.org/docs/community/contribution) before submitting the PR"
+1 -1
View File
@@ -39,4 +39,4 @@ body:
- type: markdown
attributes:
value: |
Please read the [Contribution Guideline](https://hertzbeat.com/docs/others/contributing/) before submitting the PR
Please read the [Contribution Guideline](https://hertzbeat.apache.org/docs/community/contribution) before submitting the PR
+1 -1
View File
@@ -30,5 +30,5 @@ body:
- type: markdown
attributes:
value: |
Please read the [Contribution Guideline](https://hertzbeat.apache.org/docs/others/contributing/) before submitting the PR
Please read the [Contribution Guideline](https://hertzbeat.apache.org/docs/community/contribution) before submitting the PR
@@ -1,71 +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.
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Backend CI
on:
workflow_call:
secrets:
codecov_token:
required: true
workflow_dispatch:
jobs:
backend-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./script/ci/github-actions/setup-deps
- name: Build with Maven
run: mvn clean -B package -Prelease --file pom.xml
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4.0.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
- name: Build Image
uses: docker/build-push-action@v3
with:
context: ./dist
file: ./script/docker/server/Dockerfile
push: false
tags: apache/hertzbeat:test
- name: Run E2E
run: |
sudo curl -L https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
sudo chmod u+x /usr/local/bin/docker-compose
cd e2e
sudo docker-compose version
sudo docker-compose up --exit-code-from testing --remove-orphans
# upload application logs
- name: Upload logs & API test reports
uses: actions/upload-artifact@v3
if: always()
with:
name: hz-logs-${{ github.run_id }}
path: |
e2e/logs/
e2e/report/
+41 -3
View File
@@ -40,6 +40,44 @@ on:
jobs:
backend-build:
uses: ./.github/workflows/backend-build-test-reuse.yml
secrets:
codecov_token: ${{ secrets.CODECOV_TOKEN }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./script/ci/github-actions/setup-deps
- name: Build with Maven
run: mvn clean -B package -Prelease -Dmaven.test.skip=false --file pom.xml
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4.0.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
- name: Build Image
uses: docker/build-push-action@v3
with:
context: ./dist
file: ./script/docker/server/Dockerfile
push: false
tags: apache/hertzbeat:test
- name: Run E2E
run: |
sudo curl -L https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
sudo chmod u+x /usr/local/bin/docker-compose
cd e2e
sudo docker-compose version
sudo docker-compose up --exit-code-from testing --remove-orphans
# upload application logs
- name: Upload logs & API test reports
uses: actions/upload-artifact@v3
if: always()
with:
name: hz-logs-${{ github.run_id }}
path: |
e2e/logs/
e2e/report/
+1 -1
View File
@@ -1,7 +1,7 @@
tasks:
- name: Run backend
before: cd manager
before: cd hertzbeat-manager
command: |
gp sync-await setup-backend
mvn spring-boot:run
+11 -6
View File
@@ -38,7 +38,9 @@ Even small corrections to typos are very welcome :)
3. Execute under the project root directory: `mvn clean install -DskipTests`
4. Start `springboot manager` service: `manager/src/main/java/org/apache/hertzbeat/manager/Manager.java`
4. Add VM Options: `--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED`
5. Start `springboot manager` service: `manager/src/main/java/org/apache/hertzbeat/hertzbeat-manager/Manager.java`
#### Frontend start
@@ -131,13 +133,13 @@ Add WeChat account `ahertzbeat` to pull you into the WeChat group.
## 🥐 Architecture
- **[manager](https://github.com/apache/hertzbeat/tree/master/manager)** Provide monitoring management, system management basic services.
- **[manager](https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager)** Provide monitoring management, system management basic services.
> Provides monitoring management, monitoring configuration management, system user management, etc.
- **[collector](https://github.com/apache/hertzbeat/tree/master/collector)** Provide metrics data collection services.
> Use common protocols to remotely collect and obtain peer-to-peer metrics data.
- **[warehouse](https://github.com/apache/hertzbeat/tree/master/warehouse)** Provide monitoring data warehousing services.
> Metrics data management, data query, calculation and statistics.
- **[alerter](https://github.com/apache/hertzbeat/tree/master/alerter)** Provide alert service.
- **[alerter](https://github.com/apache/hertzbeat/tree/master/hertzbeat-alerter)** Provide alert service.
> Alarm calculation trigger, monitoring status linkage, alarm configuration, and alarm notification.
- **[web-app](https://github.com/apache/hertzbeat/tree/master/web-app)** Provide web ui.
> Angular Web UI.
@@ -189,7 +191,10 @@ Add WeChat account `ahertzbeat` to pull you into the WeChat group.
3. 在项目根目录运行编译: `mvn clean install -DskipTests`
4. 启动`springboot manager`服务 `manager/src/main/java/org/apache/hertzbeat/manager/Manager.java`
4. 在 `jvm` 加入参数 `--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED`
5. 启动`springboot manager`服务 `manager/src/main/java/org/apache/hertzbeat/hertzbeat-manager/Manager.java`
#### 前端启动
@@ -281,13 +286,13 @@ Add WeChat account `ahertzbeat` to pull you into the WeChat group.
### 模块
- **[manager](https://github.com/apache/hertzbeat/tree/master/manager)** 提供监控管理,系统管理基础服务
- **[manager](https://github.com/apache/hertzbeat/tree/master/hertzbeat-manager)** 提供监控管理,系统管理基础服务
> 提供对监控的管理,监控应用配置的管理,系统用户租户后台管理等。
- **[collector](https://github.com/apache/hertzbeat/tree/master/collector)** 提供监控数据采集服务
> 使用通用协议远程采集获取对端指标数据。
- **[warehouse](https://github.com/apache/hertzbeat/tree/master/warehouse)** 提供监控数据仓储服务
> 采集指标结果数据管理,数据落盘,查询,计算统计。
- **[alerter](https://github.com/apache/hertzbeat/tree/master/alerter)** 提供告警服务
- **[alerter](https://github.com/apache/hertzbeat/tree/master/hertzbeat-alerter)** 提供告警服务
> 告警计算触发,任务状态联动,告警配置,告警通知。
- **[web-app](https://github.com/apache/hertzbeat/tree/master/web-app)** 提供可视化控制台页面
> 监控告警系统可视化控制台前端
+57 -37
View File
@@ -60,42 +60,42 @@
> Welcome everyone to contribute your customized general monitoring type YML template during use.
- [Website](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-website.yml), [Port Telnet](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-port.yml),
[Http Api](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-api.yml), [Ping Connect](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ping.yml),
[Jvm](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-jvm.yml), [SiteMap](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-fullsite.yml),
[Ssl Certificate](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ssl_cert.yml), [SpringBoot2](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-springboot2.yml),
[FTP Server](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ftp.yml), [SpringBoot3](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-springboot3.yml),
[Udp Port](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-udp_port.yml), [Dns](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-dns.yml),
[Pop3](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-pop3.yml), [Ntp](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ntp.yml),
[Api Code](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-api_code.yml), [Smtp](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-smtp.yml),
[Nginx](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-nginx.yml)
- [Mysql](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-mysql.yml), [PostgreSQL](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-postgresql.yml),
[MariaDB](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-mariadb.yml), [Redis](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redis.yml),
[ElasticSearch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-elasticsearch.yml), [SqlServer](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-sqlserver.yml),
[Oracle](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-oracle.yml), [MongoDB](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-mongodb.yml),
[DM](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-dm.yml), [OpenGauss](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-opengauss.yml),
[ClickHouse](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-clickhouse.yml), [IoTDB](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-iotdb.yml),
[Redis Cluster](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redis_cluster.yml), [Redis Sentinel](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redis_sentinel.yml)
[Doris BE](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-doris_be.yml), [Doris FE](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-doris_fe.yml),
[Memcached](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-memcached.yml), [NebulaGraph](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-nebulaGraph.yml)
- [Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-linux.yml), [Ubuntu](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ubuntu.yml),
[CentOS](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-centos.yml), [Windows](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-windows.yml),
[EulerOS](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-euleros.yml), [Fedora CoreOS](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-coreos.yml),
[OpenSUSE](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-opensuse.yml), [Rocky Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-rockylinux.yml),
[Red Hat](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redhat.yml), [FreeBSD](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-freebsd.yml),
[AlmaLinux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-almalinux.yml), [Debian Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-debian.yml)
- [Tomcat](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-tomcat.yml), [Nacos](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-nacos.yml),
[Zookeeper](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-zookeeper.yml), [RabbitMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-rabbitmq.yml),
[Flink](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-flink.yml), [Kafka](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-kafka.yml),
[ShenYu](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-shenyu.yml), [DynamicTp](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-dynamic_tp.yml),
[Jetty](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-jetty.yml), [ActiveMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-activemq.yml),
[Spring Gateway](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-spring_gateway.yml), [EMQX MQTT](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-emqx.yml),
[AirFlow](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-airflow.yml), [Hive](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-hive.yml),
[Spark](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-spark.yml), [Hadoop](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-hadoop.yml)
- [Kubernetes](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-kubernetes.yml), [Docker](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-docker.yml)
- [CiscoSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-cisco_switch.yml), [HpeSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-hpe_switch.yml),
[HuaweiSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-huawei_switch.yml), [TpLinkSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-tplink_switch.yml),
[H3cSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-h3c_switch.yml)
- [Website](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-website.yml), [Port Telnet](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-port.yml),
[Http Api](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-api.yml), [Ping Connect](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ping.yml),
[Jvm](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-jvm.yml), [SiteMap](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-fullsite.yml),
[Ssl Certificate](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ssl_cert.yml), [SpringBoot2](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-springboot2.yml),
[FTP Server](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ftp.yml), [SpringBoot3](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-springboot3.yml),
[Udp Port](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-udp_port.yml), [Dns](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dns.yml),
[Pop3](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-pop3.yml), [Ntp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ntp.yml),
[Api Code](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-api_code.yml), [Smtp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-smtp.yml),
[Nginx](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-nginx.yml)
- [Mysql](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mysql.yml), [PostgreSQL](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-postgresql.yml),
[MariaDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mariadb.yml), [Redis](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis.yml),
[ElasticSearch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-elasticsearch.yml), [SqlServer](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-sqlserver.yml),
[Oracle](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-oracle.yml), [MongoDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mongodb.yml),
[DM](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dm.yml), [OpenGauss](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-opengauss.yml),
[ClickHouse](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-clickhouse.yml), [IoTDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-iotdb.yml),
[Redis Cluster](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis_cluster.yml), [Redis Sentinel](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis_sentinel.yml)
[Doris BE](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-doris_be.yml), [Doris FE](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-doris_fe.yml),
[Memcached](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-memcached.yml), [NebulaGraph](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-nebula_graph.yml)
- [Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-linux.yml), [Ubuntu](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ubuntu.yml),
[CentOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-centos.yml), [Windows](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-windows.yml),
[EulerOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-euleros.yml), [Fedora CoreOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-coreos.yml),
[OpenSUSE](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-opensuse.yml), [Rocky Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-rockylinux.yml),
[Red Hat](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redhat.yml), [FreeBSD](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-freebsd.yml),
[AlmaLinux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-almalinux.yml), [Debian Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-debian.yml)
- [Tomcat](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-tomcat.yml), [Nacos](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-nacos.yml),
[Zookeeper](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-zookeeper.yml), [RabbitMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-rabbitmq.yml),
[Flink](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-flink.yml), [Kafka](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-kafka.yml),
[ShenYu](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-shenyu.yml), [DynamicTp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dynamic_tp.yml),
[Jetty](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-jetty.yml), [ActiveMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-activemq.yml),
[Spring Gateway](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-spring_gateway.yml), [EMQX MQTT](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-emqx.yml),
[AirFlow](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-airflow.yml), [Hive](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hive.yml),
[Spark](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-spark.yml), [Hadoop](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hadoop.yml)
- [Kubernetes](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-kubernetes.yml), [Docker](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-docker.yml)
- [CiscoSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-cisco_switch.yml), [HpeSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hpe_switch.yml),
[HuaweiSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-huawei_switch.yml), [TpLinkSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-tplink_switch.yml),
[H3cSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-h3c_switch.yml)
- And More Your Custom Template.
- Notified Support `Discord` `Slack` `Telegram` `Email` `Dingtalk` `WeChat` `FeiShu` `Webhook` `SMS` `ServerChan`.
@@ -160,7 +160,7 @@ Detailed config refer to [Install HertzBeat via Package](https://hertzbeat.apach
##### 3Start via source code
1. Local source code debugging needs to start the back-end project `manager` and the front-end project `web-app`.
2. Backendneed `maven3+`, `java17`, `lombok`, start the `manager` service.
2. Backendneed `maven3+`, `java17`, `lombok`, add VM options in IDE: ` --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED `, then start the `manager` service.
3. Webneed `nodejs npm angular-cli` environment, Run `ng serve --open` in `web-app` directory after backend startup.
4. Access `http://localhost:4200` to start, default account: `admin/hertzbeat`
@@ -475,6 +475,26 @@ Thanks to these wonderful people, welcome to join us:
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sherry-peng2333"><img src="https://avatars.githubusercontent.com/u/70619577?v=4?s=100" width="100px;" alt="sherry-peng2333"/><br /><sub><b>sherry-peng2333</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=sherry-peng2333" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lixiaobaivv"><img src="https://avatars.githubusercontent.com/u/39290771?v=4?s=100" width="100px;" alt="Yzzz"/><br /><sub><b>Yzzz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lixiaobaivv" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.bckf.cn/"><img src="https://avatars.githubusercontent.com/u/13309008?v=4?s=100" width="100px;" alt="puruidong"/><br /><sub><b>puruidong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pruidong" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shinestare"><img src="https://avatars.githubusercontent.com/u/13570619?v=4?s=100" width="100px;" alt="shinestare"/><br /><sub><b>shinestare</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=shinestare" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/po-168"><img src="https://avatars.githubusercontent.com/u/185745593?v=4?s=100" width="100px;" alt="po-168"/><br /><sub><b>po-168</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=po-168" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/All-The-Best-for"><img src="https://avatars.githubusercontent.com/u/76414672?v=4?s=100" width="100px;" alt="wbs99"/><br /><sub><b>wbs99</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=All-The-Best-for" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/starryCoder"><img src="https://avatars.githubusercontent.com/u/46510059?v=4?s=100" width="100px;" alt="starryCoder"/><br /><sub><b>starryCoder</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=starryCoder" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hasimmollah"><img src="https://avatars.githubusercontent.com/u/32538599?v=4?s=100" width="100px;" alt="hasimmollah"/><br /><sub><b>hasimmollah</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=hasimmollah" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ayu-v0"><img src="https://avatars.githubusercontent.com/u/127600988?v=4?s=100" width="100px;" alt="Ayu"/><br /><sub><b>Ayu</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ayu-v0" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Rancho-7"><img src="https://avatars.githubusercontent.com/u/59016860?v=4?s=100" width="100px;" alt="Nick Guo"/><br /><sub><b>Nick Guo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Rancho-7" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/doveLin0818"><img src="https://avatars.githubusercontent.com/u/190927907?v=4?s=100" width="100px;" alt="doveLin"/><br /><sub><b>doveLin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=doveLin0818" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://zzrl.cc/"><img src="https://avatars.githubusercontent.com/u/91836599?v=4?s=100" width="100px;" alt="yunfan24"/><br /><sub><b>yunfan24</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yunfan24" title="Code">💻</a></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>
</tbody>
</table>
+57 -37
View File
@@ -61,42 +61,42 @@
> 我们将监控采集类型(mysql,jvm,k8s)都定义为 `yml` 监控模版,用户可以导入这些模版来支持对应类型的监控!
> 欢迎大家一起贡献你使用过程中自定义的通用监控类型监控模版。
- [Website](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-website.yml), [Port Telnet](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-port.yml),
[Http Api](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-api.yml), [Ping Connect](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ping.yml),
[Jvm](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-jvm.yml), [SiteMap](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-fullsite.yml),
[Ssl Certificate](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ssl_cert.yml), [SpringBoot2](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-springboot2.yml),
[FTP Server](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ftp.yml), [SpringBoot3](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-springboot3.yml),
[Udp Port](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-udp_port.yml), [Dns](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-dns.yml),
[Pop3](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-pop3.yml), [Ntp](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ntp.yml),
[Api Code](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-api_code.yml), [Smtp](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-smtp.yml),
[Nginx](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-nginx.yml)
- [Mysql](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-mysql.yml), [PostgreSQL](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-postgresql.yml),
[MariaDB](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-mariadb.yml), [Redis](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redis.yml),
[ElasticSearch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-elasticsearch.yml), [SqlServer](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-sqlserver.yml),
[Oracle](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-oracle.yml), [MongoDB](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-mongodb.yml),
[DM](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-dm.yml), [OpenGauss](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-opengauss.yml),
[ClickHouse](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-clickhouse.yml), [IoTDB](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-iotdb.yml),
[Redis Cluster](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redis_cluster.yml), [Redis Sentinel](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redis_sentinel.yml)
[Doris BE](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-doris_be.yml), [Doris FE](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-doris_fe.yml),
[Memcached](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-memcached.yml), [NebulaGraph](https://github.com/apache/hertzbeat/blob/master/manager/src/main/resources/define/app-nebulaGraph.yml)
- [Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-linux.yml), [Ubuntu](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-ubuntu.yml),
[CentOS](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-centos.yml), [Windows](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-windows.yml),
[EulerOS](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-euleros.yml), [Fedora CoreOS](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-coreos.yml),
[OpenSUSE](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-opensuse.yml), [Rocky Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-rockylinux.yml),
[Red Hat](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-redhat.yml), [FreeBSD](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-freebsd.yml),
[AlmaLinux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-almalinux.yml), [Debian Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-debian.yml)
- [Tomcat](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-tomcat.yml), [Nacos](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-nacos.yml),
[Zookeeper](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-zookeeper.yml), [RabbitMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-rabbitmq.yml),
[Flink](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-flink.yml), [Kafka](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-kafka.yml),
[ShenYu](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-shenyu.yml), [DynamicTp](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-dynamic_tp.yml),
[Jetty](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-jetty.yml), [ActiveMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-activemq.yml),
[Spring Gateway](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-spring_gateway.yml), [EMQX MQTT](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-emqx.yml),
[AirFlow](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-airflow.yml), [Hive](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-hive.yml),
[Spark](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-spark.yml), [Hadoop](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-hadoop.yml)
- [Kubernetes](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-kubernetes.yml), [Docker](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-docker.yml)
- [CiscoSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-cisco_switch.yml), [HpeSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-hpe_switch.yml),
[HuaweiSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-huawei_switch.yml), [TpLinkSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-tplink_switch.yml),
[H3cSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/manager/src/main/resources/define/app-h3c_switch.yml)
- [Website](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-website.yml), [Port Telnet](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-port.yml),
[Http Api](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-api.yml), [Ping Connect](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ping.yml),
[Jvm](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-jvm.yml), [SiteMap](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-fullsite.yml),
[Ssl Certificate](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ssl_cert.yml), [SpringBoot2](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-springboot2.yml),
[FTP Server](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ftp.yml), [SpringBoot3](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-springboot3.yml),
[Udp Port](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-udp_port.yml), [Dns](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dns.yml),
[Pop3](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-pop3.yml), [Ntp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ntp.yml),
[Api Code](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-api_code.yml), [Smtp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-smtp.yml),
[Nginx](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-nginx.yml)
- [Mysql](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mysql.yml), [PostgreSQL](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-postgresql.yml),
[MariaDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mariadb.yml), [Redis](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis.yml),
[ElasticSearch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-elasticsearch.yml), [SqlServer](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-sqlserver.yml),
[Oracle](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-oracle.yml), [MongoDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-mongodb.yml),
[DM](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dm.yml), [OpenGauss](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-opengauss.yml),
[ClickHouse](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-clickhouse.yml), [IoTDB](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-iotdb.yml),
[Redis Cluster](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis_cluster.yml), [Redis Sentinel](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redis_sentinel.yml)
[Doris BE](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-doris_be.yml), [Doris FE](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-doris_fe.yml),
[Memcached](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-memcached.yml), [NebulaGraph](https://github.com/apache/hertzbeat/blob/master/hertzbeat-manager/src/main/resources/define/app-nebula_graph.yml)
- [Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-linux.yml), [Ubuntu](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-ubuntu.yml),
[CentOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-centos.yml), [Windows](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-windows.yml),
[EulerOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-euleros.yml), [Fedora CoreOS](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-coreos.yml),
[OpenSUSE](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-opensuse.yml), [Rocky Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-rockylinux.yml),
[Red Hat](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-redhat.yml), [FreeBSD](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-freebsd.yml),
[AlmaLinux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-almalinux.yml), [Debian Linux](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-debian.yml)
- [Tomcat](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-tomcat.yml), [Nacos](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-nacos.yml),
[Zookeeper](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-zookeeper.yml), [RabbitMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-rabbitmq.yml),
[Flink](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-flink.yml), [Kafka](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-kafka.yml),
[ShenYu](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-shenyu.yml), [DynamicTp](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-dynamic_tp.yml),
[Jetty](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-jetty.yml), [ActiveMQ](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-activemq.yml),
[Spring Gateway](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-spring_gateway.yml), [EMQX MQTT](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-emqx.yml),
[AirFlow](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-airflow.yml), [Hive](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hive.yml),
[Spark](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-spark.yml), [Hadoop](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hadoop.yml)
- [Kubernetes](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-kubernetes.yml), [Docker](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-docker.yml)
- [CiscoSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-cisco_switch.yml), [HpeSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-hpe_switch.yml),
[HuaweiSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-huawei_switch.yml), [TpLinkSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-tplink_switch.yml),
[H3cSwitch](https://raw.githubusercontent.com/apache/hertzbeat/master/hertzbeat-manager/src/main/resources/define/app-h3c_switch.yml)
- 和更多自定义监控模版。
- 通知支持 `Discord` `Slack` `Telegram` `邮件` `钉钉` `微信` `飞书` `短信` `Webhook` `Server酱`
@@ -159,7 +159,7 @@
##### 方式三:本地代码启动
1. 此为前后端分离项目,本地代码调试需要分别启动后端工程 `manager` 和前端工程 `web-app`
2. 后端:需要 `maven3+`, `java17` 和 `lombok` 环境,修改 `YML` 配置信息启动 `manager` 服务
2. 后端:需要 `maven3+`, `java17` 和 `lombok` 环境,修改 `YML` 配置信息,添加JVM参数`--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED`后启动 `manager` 服务即可。
3. 前端:需要 `nodejs npm angular-cli`环境,待本地后端启动后,在 `web-app` 目录下启动 `ng serve --open`
4. 浏览器访问 `http://localhost:4200` 即可开始,默认账号密码 `admin/hertzbeat`
@@ -474,6 +474,26 @@ Thanks these wonderful people, welcome to join us:
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sherry-peng2333"><img src="https://avatars.githubusercontent.com/u/70619577?v=4?s=100" width="100px;" alt="sherry-peng2333"/><br /><sub><b>sherry-peng2333</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=sherry-peng2333" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lixiaobaivv"><img src="https://avatars.githubusercontent.com/u/39290771?v=4?s=100" width="100px;" alt="Yzzz"/><br /><sub><b>Yzzz</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=lixiaobaivv" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.bckf.cn/"><img src="https://avatars.githubusercontent.com/u/13309008?v=4?s=100" width="100px;" alt="puruidong"/><br /><sub><b>puruidong</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=pruidong" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shinestare"><img src="https://avatars.githubusercontent.com/u/13570619?v=4?s=100" width="100px;" alt="shinestare"/><br /><sub><b>shinestare</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=shinestare" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/po-168"><img src="https://avatars.githubusercontent.com/u/185745593?v=4?s=100" width="100px;" alt="po-168"/><br /><sub><b>po-168</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=po-168" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/All-The-Best-for"><img src="https://avatars.githubusercontent.com/u/76414672?v=4?s=100" width="100px;" alt="wbs99"/><br /><sub><b>wbs99</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=All-The-Best-for" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/starryCoder"><img src="https://avatars.githubusercontent.com/u/46510059?v=4?s=100" width="100px;" alt="starryCoder"/><br /><sub><b>starryCoder</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=starryCoder" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hasimmollah"><img src="https://avatars.githubusercontent.com/u/32538599?v=4?s=100" width="100px;" alt="hasimmollah"/><br /><sub><b>hasimmollah</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=hasimmollah" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ayu-v0"><img src="https://avatars.githubusercontent.com/u/127600988?v=4?s=100" width="100px;" alt="Ayu"/><br /><sub><b>Ayu</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=ayu-v0" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Rancho-7"><img src="https://avatars.githubusercontent.com/u/59016860?v=4?s=100" width="100px;" alt="Nick Guo"/><br /><sub><b>Nick Guo</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=Rancho-7" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/doveLin0818"><img src="https://avatars.githubusercontent.com/u/190927907?v=4?s=100" width="100px;" alt="doveLin"/><br /><sub><b>doveLin</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=doveLin0818" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://zzrl.cc/"><img src="https://avatars.githubusercontent.com/u/91836599?v=4?s=100" width="100px;" alt="yunfan24"/><br /><sub><b>yunfan24</b></sub></a><br /><a href="https://github.com/apache/hertzbeat/commits?author=yunfan24" title="Code">💻</a></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>
</tbody>
</table>
+9 -6
View File
@@ -1,12 +1,9 @@
# Security Policy
**[Version Releases](https://github.com/apache/hertzbeat/releases)**
Apache HertzBeat follows the [ASF vulnerability handling process](https://apache.org/security/#vulnerability-handling)
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
|-------------------|--------------------|
| Latest Version | :white_check_mark: |
@@ -14,6 +11,12 @@ currently being supported with security updates.
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Please do not file GitHub issues for security vulnerabilities as they are public!
Please report vulnerability disclosures to `security@apache.org`.
To report a new vulnerability you have discovered please follow the [ASF vulnerability reporting process](https://apache.org/security/#reporting-a-vulnerability).
## Extra Information
- [Apache HertzBeat Security Model](https://hertzbeat.apache.org/docs/help/security_model)
- [ASF Security](https://www.apache.org/security/)
- [Apache HertzBeat Document](https://hertzbeat.apache.org/docs/)
@@ -1,483 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.alert.calculate;
import static org.apache.hertzbeat.common.constants.CommonConstants.ALERT_STATUS_CODE_NOT_REACH;
import static org.apache.hertzbeat.common.constants.CommonConstants.ALERT_STATUS_CODE_PENDING;
import static org.apache.hertzbeat.common.constants.CommonConstants.ALERT_STATUS_CODE_SOLVED;
import static org.apache.hertzbeat.common.constants.CommonConstants.TAG_CODE;
import static org.apache.hertzbeat.common.constants.CommonConstants.TAG_METRIC;
import static org.apache.hertzbeat.common.constants.CommonConstants.TAG_METRICS;
import static org.apache.hertzbeat.common.constants.CommonConstants.TAG_MONITOR_APP;
import static org.apache.hertzbeat.common.constants.CommonConstants.TAG_MONITOR_ID;
import static org.apache.hertzbeat.common.constants.CommonConstants.TAG_MONITOR_NAME;
import jakarta.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.jexl3.JexlException;
import org.apache.commons.jexl3.JexlExpression;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.alert.AlerterWorkerPool;
import org.apache.hertzbeat.alert.dao.AlertMonitorDao;
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
import org.apache.hertzbeat.alert.service.AlertDefineService;
import org.apache.hertzbeat.alert.service.AlertService;
import org.apache.hertzbeat.alert.util.AlertTemplateUtil;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.Alert;
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
import org.apache.hertzbeat.common.entity.manager.Monitor;
import org.apache.hertzbeat.common.entity.manager.TagItem;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.hertzbeat.common.queue.CommonDataQueue;
import org.apache.hertzbeat.common.support.event.MonitorDeletedEvent;
import org.apache.hertzbeat.common.support.event.SystemConfigChangeEvent;
import org.apache.hertzbeat.common.util.CommonUtil;
import org.apache.hertzbeat.common.util.JexlExpressionRunner;
import org.apache.hertzbeat.common.util.ResourceBundleUtil;
import org.springframework.context.event.EventListener;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
/**
* Calculate alarms based on the alarm definition rules and collected data
*/
@Component
@Slf4j
public class CalculateAlarm {
private static final String SYSTEM_VALUE_ROW_COUNT = "system_value_row_count";
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);
}
} 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;
}
List<CollectRep.Field> fields = metricsData.getFieldsList();
Map<String, Object> fieldValueMap = new HashMap<>(8);
int valueRowCount = metricsData.getValuesCount();
for (Map.Entry<String, List<AlertDefine>> entry : defineMap.entrySet()) {
List<AlertDefine> defines = entry.getValue();
for (AlertDefine define : defines) {
final String expr = define.getExpr();
if (StringUtils.isBlank(expr)) {
continue;
}
if (expr.contains(SYSTEM_VALUE_ROW_COUNT) && metricsData.getValuesCount() == 0) {
fieldValueMap.put(SYSTEM_VALUE_ROW_COUNT, valueRowCount);
try {
boolean match = execAlertExpression(fieldValueMap, expr);
try {
if (match) {
// If the threshold rule matches, the number of times the threshold has been triggered is determined and an alarm is triggered
afterThresholdRuleMatch(currentTimeMilli, monitorId, app, metrics, "", fieldValueMap, define);
// if the threshold is triggered, ignore other data rows
continue;
} else {
String alarmKey = String.valueOf(monitorId) + define.getId();
triggeredAlertMap.remove(alarmKey);
if (define.isRecoverNotice()) {
handleRecoveredAlert(currentTimeMilli, define, expr, alarmKey);
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
} catch (Exception ignored) {}
}
for (CollectRep.ValueRow valueRow : metricsData.getValuesList()) {
if (CollectionUtils.isEmpty(valueRow.getColumnsList())) {
continue;
}
fieldValueMap.clear();
fieldValueMap.put(SYSTEM_VALUE_ROW_COUNT, valueRowCount);
StringBuilder tagBuilder = new StringBuilder();
for (int index = 0; index < valueRow.getColumnsList().size(); index++) {
String valueStr = valueRow.getColumns(index);
if (CommonConstants.NULL_VALUE.equals(valueStr)) {
continue;
}
final CollectRep.Field field = fields.get(index);
final int fieldType = field.getType();
if (fieldType == CommonConstants.TYPE_NUMBER) {
final Double doubleValue;
if ((doubleValue = CommonUtil.parseStrDouble(valueStr)) != null) {
fieldValueMap.put(field.getName(), doubleValue);
}
} else if (fieldType == CommonConstants.TYPE_TIME) {
final Integer integerValue;
if ((integerValue = CommonUtil.parseStrInteger(valueStr)) != null) {
fieldValueMap.put(field.getName(), integerValue);
}
} else {
if (StringUtils.isNotEmpty(valueStr)) {
fieldValueMap.put(field.getName(), valueStr);
}
}
if (field.getLabel()) {
tagBuilder.append("-").append(valueStr);
}
}
try {
boolean match = execAlertExpression(fieldValueMap, expr);
try {
if (match) {
// If the threshold rule matches, the number of times the threshold has been triggered is determined and an alarm is triggered
afterThresholdRuleMatch(currentTimeMilli, monitorId, app, metrics, tagBuilder.toString(), fieldValueMap, define);
} else {
String alarmKey = String.valueOf(monitorId) + define.getId() + tagBuilder;
triggeredAlertMap.remove(alarmKey);
if (define.isRecoverNotice()) {
handleRecoveredAlert(currentTimeMilli, define, expr, alarmKey);
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
} catch (Exception ignored) {}
}
}
}
}
private void handleRecoveredAlert(long currentTimeMilli, AlertDefine define, String expr, String alarmKey) {
Alert notResolvedAlert = notRecoveredAlertMap.remove(alarmKey);
if (notResolvedAlert != null) {
// Sending an alarm Restore
Map<String, String> tags = notResolvedAlert.getTags();
String content = this.bundle.getString("alerter.alarm.recover") + " : " + expr;
Alert resumeAlert = Alert.builder()
.tags(tags)
.target(define.getApp() + "." + define.getMetric() + "." + define.getField())
.content(content)
.priority(CommonConstants.ALERT_PRIORITY_CODE_WARNING)
.status(CommonConstants.ALERT_STATUS_CODE_RESTORED)
.firstAlarmTime(currentTimeMilli)
.lastAlarmTime(notResolvedAlert.getLastAlarmTime())
.triggerTimes(1)
.build();
alarmCommonReduce.reduceAndSendAlarm(resumeAlert);
}
}
private void afterThresholdRuleMatch(long currentTimeMilli, long monitorId, String app, String metrics, String tagStr,
Map<String, Object> fieldValueMap, AlertDefine define) {
String alarmKey = String.valueOf(monitorId) + define.getId() + tagStr;
Alert triggeredAlert = triggeredAlertMap.get(alarmKey);
if (triggeredAlert != null) {
int times = triggeredAlert.getTriggerTimes() + 1;
triggeredAlert.setTriggerTimes(times);
triggeredAlert.setFirstAlarmTime(currentTimeMilli);
triggeredAlert.setLastAlarmTime(currentTimeMilli);
int defineTimes = define.getTimes() == null ? 1 : define.getTimes();
if (times >= defineTimes) {
triggeredAlert.setStatus(ALERT_STATUS_CODE_PENDING);
triggeredAlertMap.remove(alarmKey);
notRecoveredAlertMap.put(alarmKey, triggeredAlert);
alarmCommonReduce.reduceAndSendAlarm(triggeredAlert.clone());
}
} else {
fieldValueMap.put(TAG_MONITOR_APP, app);
fieldValueMap.put(TAG_METRICS, metrics);
fieldValueMap.put(TAG_METRIC, define.getField());
Map<String, String> tags = new HashMap<>(8);
tags.put(TAG_MONITOR_ID, String.valueOf(monitorId));
tags.put(TAG_MONITOR_APP, app);
tags.put(CommonConstants.TAG_THRESHOLD_ID, String.valueOf(define.getId()));
if (!CollectionUtils.isEmpty(define.getTags())) {
for (TagItem tagItem : define.getTags()) {
fieldValueMap.put(tagItem.getName(), tagItem.getValue());
tags.put(tagItem.getName(), tagItem.getValue());
}
}
Alert alert = Alert.builder()
.tags(tags)
.priority(define.getPriority())
.status(ALERT_STATUS_CODE_NOT_REACH)
.target(app + "." + metrics + "." + define.getField())
.triggerTimes(1)
.firstAlarmTime(currentTimeMilli)
.lastAlarmTime(currentTimeMilli)
// Keyword matching and substitution in the template
.content(AlertTemplateUtil.render(define.getTemplate(), fieldValueMap))
.build();
int defineTimes = define.getTimes() == null ? 1 : define.getTimes();
if (1 >= defineTimes) {
alert.setStatus(ALERT_STATUS_CODE_PENDING);
notRecoveredAlertMap.put(alarmKey, alert);
alarmCommonReduce.reduceAndSendAlarm(alert.clone());
} else {
triggeredAlertMap.put(alarmKey, alert);
}
}
}
private boolean execAlertExpression(Map<String, Object> fieldValueMap, String expr) {
Boolean match;
JexlExpression expression;
try {
expression = JexlExpressionRunner.compile(expr);
} catch (JexlException jexlException) {
log.error("Alarm Rule: {} Compile Error: {}.", expr, jexlException.getMessage());
throw jexlException;
} catch (Exception e) {
log.error("Alarm Rule: {} Unknown Error: {}.", expr, e.getMessage());
throw e;
}
try {
match = (Boolean) JexlExpressionRunner.evaluate(expression, fieldValueMap);
} catch (JexlException jexlException) {
log.error("Alarm Rule: {} Run Error: {}.", expr, jexlException.getMessage());
throw jexlException;
} catch (Exception e) {
log.error("Alarm Rule: {} Unknown Error: {}.", expr, e.getMessage());
throw e;
}
return match != null && match;
}
private void handlerAvailableMetrics(long monitorId, String app, CollectRep.MetricsData metricsData) {
if (metricsData.getCode() == CollectRep.Code.TIMEOUT) {
return;
}
// TODO CACHE getMonitorBindAlertAvaDefine
AlertDefine avaAlertDefine = alertDefineService.getMonitorBindAlertAvaDefine(monitorId, app, CommonConstants.AVAILABILITY);
if (avaAlertDefine == null) {
return;
}
long currentTimeMill = System.currentTimeMillis();
if (metricsData.getCode() != CollectRep.Code.SUCCESS) {
Alert preAlert = triggeredAlertMap.get(String.valueOf(monitorId));
Map<String, String> tags = new HashMap<>(6);
tags.put(TAG_MONITOR_ID, String.valueOf(monitorId));
tags.put(TAG_MONITOR_APP, app);
tags.put(CommonConstants.TAG_THRESHOLD_ID, String.valueOf(avaAlertDefine.getId()));
tags.put(TAG_METRICS, CommonConstants.AVAILABILITY);
tags.put(TAG_CODE, metricsData.getCode().name());
Map<String, Object> valueMap = tags.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
if (!CollectionUtils.isEmpty(avaAlertDefine.getTags())) {
for (TagItem tagItem : avaAlertDefine.getTags()) {
valueMap.put(tagItem.getName(), tagItem.getValue());
tags.put(tagItem.getName(), tagItem.getValue());
}
}
if (preAlert == null) {
Alert.AlertBuilder alertBuilder = Alert.builder()
.tags(tags)
.priority(avaAlertDefine.getPriority())
.status(ALERT_STATUS_CODE_NOT_REACH)
.target(CommonConstants.AVAILABILITY)
.content(AlertTemplateUtil.render(avaAlertDefine.getTemplate(), valueMap))
.firstAlarmTime(currentTimeMill)
.lastAlarmTime(currentTimeMill)
.triggerTimes(1);
if (avaAlertDefine.getTimes() == null || avaAlertDefine.getTimes() <= 1) {
String notResolvedAlertKey = monitorId + CommonConstants.AVAILABILITY;
alertBuilder.status(ALERT_STATUS_CODE_PENDING);
notRecoveredAlertMap.put(notResolvedAlertKey, alertBuilder.build());
alarmCommonReduce.reduceAndSendAlarm(alertBuilder.build());
} else {
triggeredAlertMap.put(String.valueOf(monitorId), alertBuilder.build());
}
} else {
int times = preAlert.getTriggerTimes() + 1;
preAlert.setTriggerTimes(times);
preAlert.setFirstAlarmTime(currentTimeMill);
preAlert.setLastAlarmTime(currentTimeMill);
int defineTimes = avaAlertDefine.getTimes() == null ? 1 : avaAlertDefine.getTimes();
if (times >= defineTimes) {
preAlert.setStatus(ALERT_STATUS_CODE_PENDING);
String notResolvedAlertKey = monitorId + CommonConstants.AVAILABILITY;
notRecoveredAlertMap.put(notResolvedAlertKey, preAlert.clone());
alarmCommonReduce.reduceAndSendAlarm(preAlert.clone());
triggeredAlertMap.remove(String.valueOf(monitorId));
}
}
} else {
// Check whether an availability or unreachable alarm is generated before the association monitoring
// and send a clear alarm to clear the monitoring status
triggeredAlertMap.remove(String.valueOf(monitorId));
String notResolvedAlertKey = monitorId + CommonConstants.AVAILABILITY;
Alert notResolvedAlert = notRecoveredAlertMap.remove(notResolvedAlertKey);
if (notResolvedAlert != null) {
// Sending an alarm Restore
Map<String, String> tags = notResolvedAlert.getTags();
if (!avaAlertDefine.isRecoverNotice()) {
tags.put(CommonConstants.IGNORE, CommonConstants.IGNORE);
}
String content = this.bundle.getString("alerter.availability.recover");
Alert resumeAlert = Alert.builder()
.tags(tags)
.target(CommonConstants.AVAILABILITY)
.content(content)
.priority(CommonConstants.ALERT_PRIORITY_CODE_WARNING)
.status(CommonConstants.ALERT_STATUS_CODE_RESTORED)
.firstAlarmTime(currentTimeMill)
.lastAlarmTime(notResolvedAlert.getLastAlarmTime())
.triggerTimes(1)
.build();
alarmCommonReduce.reduceAndSendAlarm(resumeAlert);
Runnable updateStatusJob = () -> {
// todo update pre all type alarm status
updateAvailabilityAlertStatus(monitorId, resumeAlert);
};
workerPool.executeJob(updateStatusJob);
}
}
}
private void updateAvailabilityAlertStatus(long monitorId, Alert restoreAlert) {
List<Alert> availabilityAlerts = queryAvailabilityAlerts(monitorId, restoreAlert);
availabilityAlerts.stream().parallel().forEach(alert -> {
log.info("updating alert status solved id: {}", alert.getId());
alertService.editAlertStatus(ALERT_STATUS_CODE_SOLVED, List.of(alert.getId()));
});
}
private List<Alert> queryAvailabilityAlerts(long monitorId, Alert restoreAlert) {
//create query condition
Specification<Alert> specification = (root, query, criteriaBuilder) -> {
List<Predicate> andList = new ArrayList<>();
Predicate predicateTags = criteriaBuilder.like(root.get("tags").as(String.class), "%" + monitorId + "%");
andList.add(predicateTags);
Predicate predicatePriority = criteriaBuilder.equal(root.get("priority"), CommonConstants.ALERT_PRIORITY_CODE_EMERGENCY);
andList.add(predicatePriority);
Predicate predicateStatus = criteriaBuilder.equal(root.get("status"), ALERT_STATUS_CODE_PENDING);
andList.add(predicateStatus);
Predicate predicateAlertTime = criteriaBuilder.lessThanOrEqualTo(root.get("lastAlarmTime"), restoreAlert.getLastAlarmTime());
andList.add(predicateAlertTime);
Predicate[] predicates = new Predicate[andList.size()];
return criteriaBuilder.and(andList.toArray(predicates));
};
//query results
return alertService.getAlerts(specification);
}
@EventListener(SystemConfigChangeEvent.class)
public void onSystemConfigChangeEvent(SystemConfigChangeEvent event) {
log.info("calculate alarm receive system config change event: {}.", event.getSource());
this.bundle = ResourceBundleUtil.getBundle("alerter");
}
@EventListener(MonitorDeletedEvent.class)
public void onMonitorDeletedEvent(MonitorDeletedEvent event) {
log.info("calculate alarm receive monitor {} has been deleted.", event.getMonitorId());
this.triggeredAlertMap.remove(String.valueOf(event.getMonitorId()));
}
}
@@ -1,63 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.alert.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 lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.dto.GeneralCloudAlertReport;
import org.apache.hertzbeat.alert.service.AlertService;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Extern Alarm Manage API
*/
@Tag(name = "Extern Alarm Manage API")
@RestController
@RequestMapping(path = "/api/alerts/report", produces = {APPLICATION_JSON_VALUE})
@Slf4j
public class AlertReportController {
@Autowired
private AlertService alertService;
@PostMapping("/{cloud}")
@Operation(summary = "Interface for reporting external alarm information of cloud service")
public ResponseEntity<Message<Void>> addNewAlertReportFromCloud(@PathVariable("cloud") String cloudServiceName,
@RequestBody String alertReport) {
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"));
}
}
@@ -1,78 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.alert.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.Map;
import org.apache.hertzbeat.common.entity.dto.AlertReport;
/**
* Abstract parent of cloud service alarm information
* {@link AlertReport} - The method corresponds to the parameter of this class one by one. For details about the method, see this class
*/
public abstract class CloudAlertReportAbstract {
/**
* This parameter specifies the alarm name when a cloud alarm is converted to an internal alarm
*/
@JsonIgnore
public abstract String getAlertName();
/**
* Interval when a cloud alarm is converted to an internal alarm
*/
@JsonIgnore
public abstract Integer getAlertDuration();
/**
* Specifies the alarm time when a cloud alarm is converted to an internal alarm
*/
@JsonIgnore
public abstract long getAlertTime();
/**
* Severity of an alarm when a cloud alarm is converted to an internal alarm
*/
@JsonIgnore
public abstract Integer getPriority();
/**
* This parameter specifies the alarm type when a cloud alarm is converted to an internal alarm
*/
@JsonIgnore
public abstract Integer getReportType();
/**
* Alarm label information about a cloud alarm that is converted to an internal alarm
*/
@JsonIgnore
public abstract Map<String, String> getLabels();
/**
* Alarm labels when a cloud alarm is converted to an internal alarm
*/
@JsonIgnore
public abstract Map<String, String> getAnnotations();
/**
* This topic describes the alarm content when a cloud alarm is converted to an internal alarm
*/
@JsonIgnore
public abstract String getContent();
}
@@ -1,79 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.alert.dto;
import java.util.Optional;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.hertzbeat.alert.util.DateUtil;
import org.apache.hertzbeat.common.entity.dto.AlertReport;
/**
* Generic cloud alarm entity class
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class GeneralCloudAlertReport extends AlertReport {
/**
* Alarm date and time
*/
private String alertDateTime;
/**
* DATE TIME FORMAT
*/
private String dateTimeFormat;
/**
* You can refresh the timestamp of the alarm time with enhanced properties
*/
public void refreshAlertTime() {
if (getAlertTime() != 0L) {
return;
}
if (StringUtils.isNotBlank(alertDateTime)) {
Long timeStamp = null;
if (StringUtils.isNotBlank(dateTimeFormat)) {
Optional<Long> tsf = DateUtil.getTimeStampFromFormat(alertDateTime, dateTimeFormat);
boolean present = tsf.isPresent();
if (present) {
timeStamp = tsf.get();
}
}
if (timeStamp == null) {
Optional<Long> tsf = DateUtil.getTimeStampFromSomeFormats(alertDateTime);
boolean present = tsf.isPresent();
if (present) {
timeStamp = tsf.get();
}
}
if (timeStamp != null) {
setAlertTime(timeStamp);
return;
}
}
throw new RuntimeException("parse alarm time error");
}
}
@@ -1,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();
}
}
@@ -1,73 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.alert.reduce;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.dao.AlertMonitorDao;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.Alert;
import org.apache.hertzbeat.common.entity.manager.Tag;
import org.apache.hertzbeat.common.queue.CommonDataQueue;
import org.springframework.stereotype.Service;
/**
* reduce alarm and send alert data
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class AlarmCommonReduce {
private final AlarmSilenceReduce alarmSilenceReduce;
private final AlarmConvergeReduce alarmConvergeReduce;
private final CommonDataQueue dataQueue;
private final AlertMonitorDao alertMonitorDao;
public void reduceAndSendAlarm(Alert alert) {
alert.setTimes(1);
Map<String, String> tags = alert.getTags();
if (tags == null) {
tags = new HashMap<>(8);
alert.setTags(tags);
}
String monitorIdStr = tags.get(CommonConstants.TAG_MONITOR_ID);
if (monitorIdStr == null) {
log.debug("receiver extern alarm message: {}", alert);
} else {
long monitorId = Long.parseLong(monitorIdStr);
List<Tag> tagList = alertMonitorDao.findMonitorIdBindTags(monitorId);
for (Tag tag : tagList) {
if (!tags.containsKey(tag.getName())) {
tags.put(tag.getName(), tag.getTagValue());
}
}
}
// converge -> silence
if (alarmConvergeReduce.filterConverge(alert) && alarmSilenceReduce.filterSilence(alert)) {
dataQueue.sendAlertsData(alert);
}
}
}
@@ -1,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]));
}
}
@@ -1,125 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.alert.reduce;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.apache.hertzbeat.alert.dao.AlertSilenceDao;
import org.apache.hertzbeat.common.cache.CacheFactory;
import org.apache.hertzbeat.common.cache.CommonCacheService;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.Alert;
import org.apache.hertzbeat.common.entity.alerter.AlertSilence;
import org.apache.hertzbeat.common.entity.manager.TagItem;
import org.springframework.stereotype.Service;
/**
* silence alarm
*/
@Service
@RequiredArgsConstructor
public class AlarmSilenceReduce {
private final AlertSilenceDao alertSilenceDao;
/**
* alert silence filter data
* @param alert alert
* @return true when not filter
*/
@SuppressWarnings("unchecked")
public boolean filterSilence(Alert alert) {
CommonCacheService<String, Object> silenceCache = CacheFactory.getAlertSilenceCache();
List<AlertSilence> alertSilenceList = (List<AlertSilence>) silenceCache.get(CommonConstants.CACHE_ALERT_SILENCE);
if (alertSilenceList == null) {
alertSilenceList = alertSilenceDao.findAll();
silenceCache.put(CommonConstants.CACHE_ALERT_SILENCE, alertSilenceList);
}
for (AlertSilence alertSilence : alertSilenceList) {
if (!alertSilence.isEnable()) {
continue;
}
// if match the silence rule, return
boolean match = alertSilence.isMatchAll();
if (!match) {
List<TagItem> tags = alertSilence.getTags();
if (alert.getTags() != null && !alert.getTags().isEmpty()) {
Map<String, String> alertTagMap = alert.getTags();
match = tags.stream().anyMatch(item -> {
if (alertTagMap.containsKey(item.getName())) {
String tagValue = alertTagMap.get(item.getName());
if (tagValue == null && item.getValue() == null) {
return true;
} else {
return tagValue != null && tagValue.equals(item.getValue());
}
} else {
return false;
}
});
} else {
match = true;
}
if (match && alertSilence.getPriorities() != null && !alertSilence.getPriorities().isEmpty()) {
match = alertSilence.getPriorities().stream().anyMatch(item -> item != null && item == alert.getPriority());
}
}
if (match) {
if (alertSilence.getType() == 0) {
// once time
return checkAndSave(LocalDateTime.now(), alertSilence);
} 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);
}
}
}
}
}
return true;
}
/**
* Check AlertSilence start and end match, to save alertSilence obj.
* @param times LocalDateTime.
* @param alertSilence {@link AlertSilence}
* @return boolean
*/
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());
if (startMatch && endMatch) {
int time = Optional.ofNullable(alertSilence.getTimes()).orElse(0);
alertSilence.setTimes(time + 1);
alertSilenceDao.save(alertSilence);
return false;
}
return true;
}
}
@@ -1,236 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.alert.service.impl;
import jakarta.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.Date;
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.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.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;
/**
* Realization of Alarm Information Service
*/
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class AlertServiceImpl implements AlertService {
@Autowired
private AlertDao alertDao;
@Autowired
private AlarmCommonReduce alarmCommonReduce;
@Override
public void addAlert(Alert alert) throws RuntimeException {
alertDao.save(alert);
}
@Override
public Page<Alert> getAlerts(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) -> {
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[] 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);
}
@Override
public void deleteAlerts(HashSet<Long> ids) {
alertDao.deleteAlertsByIdIn(ids);
}
@Override
public void clearAlerts() {
alertDao.deleteAll();
}
@Override
public void editAlertStatus(Byte status, List<Long> ids) {
alertDao.updateAlertsStatus(status, ids);
}
@Override
public AlertSummary getAlertsSummary() {
AlertSummary alertSummary = new AlertSummary();
// Statistics on the alarm information in the alarm state
List<AlertPriorityNum> priorityNums = alertDao.findAlertPriorityNum();
if (priorityNums != null) {
for (AlertPriorityNum priorityNum : priorityNums) {
switch (priorityNum.getPriority()) {
case CommonConstants
.ALERT_PRIORITY_CODE_WARNING -> alertSummary.setPriorityWarningNum(priorityNum.getNum());
case CommonConstants.ALERT_PRIORITY_CODE_CRITICAL -> alertSummary.setPriorityCriticalNum(priorityNum.getNum());
case CommonConstants.ALERT_PRIORITY_CODE_EMERGENCY -> alertSummary.setPriorityEmergencyNum(priorityNum.getNum());
default -> {}
}
}
}
long total = alertDao.count();
alertSummary.setTotal(total);
long dealNum = total - alertSummary.getPriorityCriticalNum()
- alertSummary.getPriorityEmergencyNum() - alertSummary.getPriorityWarningNum();
alertSummary.setDealNum(dealNum);
try {
if (total == 0) {
alertSummary.setRate(100);
} else {
float rate = BigDecimal.valueOf(100 * (float) dealNum / total)
.setScale(2, RoundingMode.HALF_UP)
.floatValue();
alertSummary.setRate(rate);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return alertSummary;
}
@Override
public void addNewAlertReport(AlertReport alertReport) {
alarmCommonReduce.reduceAndSendAlarm(buildAlertData(alertReport));
}
@Override
public 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(new Date().getTime())
.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,134 +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 static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
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.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;
/**
* test case for {@link AlarmCommonReduce}
*/
@ExtendWith(MockitoExtension.class)
class AlarmCommonReduceTest {
@Mock
private AlarmSilenceReduce alarmSilenceReduce;
@Mock
private AlarmConvergeReduce alarmConvergeReduce;
@Mock
private CommonDataQueue dataQueue;
@Mock
private AlertMonitorDao alertMonitorDao;
private AlarmCommonReduce alarmCommonReduce;
private Alert testAlert;
@BeforeEach
void setUp() {
testAlert = Alert.builder().build();
alarmCommonReduce = new AlarmCommonReduce(
alarmSilenceReduce,
alarmConvergeReduce,
dataQueue,
alertMonitorDao
);
}
@Test
void testReduceAndSendAlarmNoMonitorId() {
when(alarmConvergeReduce.filterConverge(testAlert)).thenReturn(true);
when(alarmSilenceReduce.filterSilence(testAlert)).thenReturn(true);
alarmCommonReduce.reduceAndSendAlarm(testAlert);
verify(dataQueue).sendAlertsData(testAlert);
verify(alertMonitorDao, never()).findMonitorIdBindTags(anyLong());
}
@Test
void testReduceAndSendAlarmWithMonitorId() {
Map<String, String> tags = new HashMap<>();
tags.put(CommonConstants.TAG_MONITOR_ID, "123");
testAlert.setTags(tags);
doReturn(Collections.singletonList(
Tag.builder()
.name("newTag")
.tagValue("tagValue")
.build())
).when(alertMonitorDao).findMonitorIdBindTags(123L);
when(alarmConvergeReduce.filterConverge(testAlert)).thenReturn(true);
when(alarmSilenceReduce.filterSilence(testAlert)).thenReturn(true);
alarmCommonReduce.reduceAndSendAlarm(testAlert);
assertTrue(testAlert.getTags().containsKey("newTag"));
assertEquals("tagValue", testAlert.getTags().get("newTag"));
verify(dataQueue).sendAlertsData(testAlert);
}
@Test
void testReduceAndSendAlarmConvergeFilterFail() {
when(alarmConvergeReduce.filterConverge(testAlert)).thenReturn(false);
alarmCommonReduce.reduceAndSendAlarm(testAlert);
verify(dataQueue, never()).sendAlertsData(testAlert);
verify(alarmSilenceReduce, never()).filterSilence(any(Alert.class));
}
@Test
void testReduceAndSendAlarmSilenceFilterFail() {
when(alarmConvergeReduce.filterConverge(testAlert)).thenReturn(true);
when(alarmSilenceReduce.filterSilence(testAlert)).thenReturn(false);
alarmCommonReduce.reduceAndSendAlarm(testAlert);
verify(dataQueue, never()).sendAlertsData(testAlert);
}
}
@@ -1,112 +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 static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.HashMap;
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.junit.jupiter.api.AfterEach;
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.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
/**
* test case for {@link AlarmConvergeReduce}
*/
@ExtendWith(MockitoExtension.class)
class AlarmConvergeReduceTest {
@Mock
private AlertConvergeDao alertConvergeDao;
@Mock
private CommonCacheService<String, Object> convergeCache;
private AlarmConvergeReduce alarmConvergeReduce;
private Alert testAlert;
private MockedStatic<CacheFactory> cacheFactoryMockedStatic;
@BeforeEach
void setUp() {
testAlert = Alert.builder()
.tags(new HashMap<>())
.status(CommonConstants.ALERT_STATUS_CODE_SOLVED)
.build();
cacheFactoryMockedStatic = mockStatic(CacheFactory.class);
cacheFactoryMockedStatic.when(CacheFactory::getAlertConvergeCache).thenReturn(convergeCache);
alarmConvergeReduce = new AlarmConvergeReduce(alertConvergeDao);
}
@AfterEach
void tearDown() {
if (cacheFactoryMockedStatic != null) {
cacheFactoryMockedStatic.close();
}
}
@Test
void testFilterConverge_RestoredAlert() {
testAlert.setStatus(CommonConstants.ALERT_STATUS_CODE_RESTORED);
boolean result = alarmConvergeReduce.filterConverge(testAlert);
assertTrue(result);
}
@Test
void testFilterConverge_IgnoreTag() {
testAlert.getTags().put(CommonConstants.IGNORE, "true");
boolean result = alarmConvergeReduce.filterConverge(testAlert);
assertTrue(result);
}
@Test
void testFilterConvergeNoConverge() {
when(convergeCache.get(CommonConstants.CACHE_ALERT_CONVERGE)).thenReturn(null);
when(alertConvergeDao.findAll()).thenReturn(Collections.emptyList());
boolean result = alarmConvergeReduce.filterConverge(testAlert);
assertTrue(result);
verify(convergeCache).get(CommonConstants.CACHE_ALERT_CONVERGE);
verify(alertConvergeDao).findAll();
verify(convergeCache).put(CommonConstants.CACHE_ALERT_CONVERGE, Collections.emptyList());
}
}
@@ -1,186 +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 static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Collections;
import java.util.HashMap;
import org.apache.hertzbeat.alert.dao.AlertSilenceDao;
import org.apache.hertzbeat.common.cache.CacheFactory;
import org.apache.hertzbeat.common.cache.CommonCacheService;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.alerter.Alert;
import org.apache.hertzbeat.common.entity.alerter.AlertSilence;
import org.apache.hertzbeat.common.entity.manager.TagItem;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.MockitoAnnotations;
/**
* test case for {@link AlarmSilenceReduce}
*/
class AlarmSilenceReduceTest {
@Mock
private AlertSilenceDao alertSilenceDao;
@Mock
private CommonCacheService<String, Object> silenceCache;
private AlarmSilenceReduce alarmSilenceReduce;
private MockedStatic<CacheFactory> cacheFactoryMockedStatic;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
cacheFactoryMockedStatic = mockStatic(CacheFactory.class);
cacheFactoryMockedStatic.when(CacheFactory::getAlertSilenceCache).thenReturn(silenceCache);
// inject dao object.
alarmSilenceReduce = new AlarmSilenceReduce(alertSilenceDao);
}
@Test
void testFilterSilenceNull() {
// when cache get result is null, exec db logic.
when(silenceCache.get(CommonConstants.CACHE_ALERT_SILENCE)).thenReturn(null);
doReturn(Collections.emptyList()).when(alertSilenceDao).findAll();
Alert alert = Alert.builder()
.tags(new HashMap<>())
.priority((byte) 1)
.build();
boolean result = alarmSilenceReduce.filterSilence(alert);
assertTrue(result);
verify(alertSilenceDao, times(1)).findAll();
verify(silenceCache, times(1)).put(eq(CommonConstants.CACHE_ALERT_SILENCE), any());
}
@Test
void testFilterSilenceOnce() {
AlertSilence alertSilence = AlertSilence.builder()
.enable(Boolean.TRUE)
.matchAll(Boolean.TRUE)
.type((byte) 0)
.periodEnd(LocalDateTime.now().atZone(ZoneId.systemDefault()).plusHours(1))
.periodStart(LocalDateTime.now().atZone(ZoneId.systemDefault()).minusHours(1))
.times(0)
.build();
when(silenceCache.get(CommonConstants.CACHE_ALERT_SILENCE)).thenReturn(Collections.singletonList(alertSilence));
doReturn(alertSilence).when(alertSilenceDao).save(alertSilence);
Alert alert = Alert.builder()
.tags(new HashMap<>())
.priority((byte) 1)
.build();
boolean result = alarmSilenceReduce.filterSilence(alert);
assertFalse(result);
verify(alertSilenceDao, times(1)).save(alertSilence);
assertEquals(1, alertSilence.getTimes());
}
@Test
void testFilterSilenceCyc() {
AlertSilence alertSilence = AlertSilence.builder()
.enable(Boolean.TRUE)
.matchAll(Boolean.TRUE)
.type((byte) 1) // cyc time
.periodEnd(LocalDateTime.now().atZone(ZoneId.systemDefault()).plusHours(1))
.periodStart(LocalDateTime.now().atZone(ZoneId.systemDefault()).minusHours(1))
.times(0)
.days(Collections.singletonList((byte) LocalDateTime.now().getDayOfWeek().getValue()))
.build();
when(silenceCache.get(CommonConstants.CACHE_ALERT_SILENCE)).thenReturn(Collections.singletonList(alertSilence));
doReturn(alertSilence).when(alertSilenceDao).save(alertSilence);
Alert alert = Alert.builder()
.tags(new HashMap<>())
.priority((byte) 1)
.build();
boolean result = alarmSilenceReduce.filterSilence(alert);
assertFalse(result);
verify(alertSilenceDao, times(1)).save(alertSilence);
assertEquals(1, alertSilence.getTimes());
}
@Test
void testFilterSilenceNoMatch() {
AlertSilence alertSilence = AlertSilence.builder()
.enable(Boolean.TRUE)
.matchAll(Boolean.TRUE)
.type((byte) 0)
.tags(Collections.singletonList(new TagItem("non-matching-tag", "value")))
.periodEnd(LocalDateTime.now().atZone(ZoneId.systemDefault()).minusHours(1))
.periodStart(LocalDateTime.now().atZone(ZoneId.systemDefault()).plusHours(1))
.times(0)
.build();
when(silenceCache.get(CommonConstants.CACHE_ALERT_SILENCE)).thenReturn(Collections.singletonList(alertSilence));
doReturn(alertSilence).when(alertSilenceDao).save(alertSilence);
Alert alert = Alert.builder()
.tags(new HashMap<>())
.priority((byte) 1)
.build();
boolean result = alarmSilenceReduce.filterSilence(alert);
assertTrue(result);
verify(alertSilenceDao, never()).save(any());
}
@AfterEach
public void tearDown() {
if (cacheFactoryMockedStatic != null) {
cacheFactoryMockedStatic.close();
}
}
}
@@ -1,139 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.alert.service;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import org.apache.hertzbeat.alert.dao.AlertDao;
import org.apache.hertzbeat.alert.dto.AlertPriorityNum;
import org.apache.hertzbeat.alert.dto.TenCloudAlertReport;
import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
import org.apache.hertzbeat.alert.service.impl.AlertServiceImpl;
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.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
/**
* Test case for {@link AlertService}
*/
@ExtendWith(MockitoExtension.class)
class AlertServiceTest {
@Mock
private AlertDao alertDao;
@Mock
private AlarmCommonReduce alarmCommonReduce;
@InjectMocks
private AlertServiceImpl alertService;
@BeforeEach
void setUp() {
}
@Test
void addAlert() {
Alert alert = new Alert();
assertDoesNotThrow(() -> alertService.addAlert(alert));
verify(alertDao, times(1)).save(alert);
}
@Test
void getAlerts() {
// todo
}
@Test
void deleteAlerts() {
HashSet<Long> ids = new HashSet<>();
ids.add(1L);
ids.add(2L);
assertDoesNotThrow(() -> alertService.deleteAlerts(ids));
verify(alertDao, times(1)).deleteAlertsByIdIn(ids);
}
@Test
void clearAlerts() {
assertDoesNotThrow(() -> alertService.clearAlerts());
verify(alertDao, times(1)).deleteAll();
}
@Test
void editAlertStatus() {
Byte status = 0;
List<Long> ids = List.of(1L, 2L, 3L);
assertDoesNotThrow(() -> alertService.editAlertStatus(status, ids));
verify(alertDao, times(1)).updateAlertsStatus(status, ids);
}
@Test
void getAlertsSummary() {
List<AlertPriorityNum> priorityNums = new ArrayList<>();
priorityNums.add(new AlertPriorityNum((byte) 1, 100));
when(alertDao.findAlertPriorityNum()).thenReturn(priorityNums);
assertDoesNotThrow(() -> alertService.getAlertsSummary());
verify(alertDao, times(1)).findAlertPriorityNum();
verify(alertDao, times(1)).count();
assertNotNull(alertService.getAlertsSummary());
}
@Test
void addNewAlertReport() {
AlertReport alertReport = AlertReport.builder()
.annotations(new HashMap<>())
.priority(0)
.alertTime(System.currentTimeMillis())
.build();
assertDoesNotThrow(() -> alertService.addNewAlertReport(alertReport));
verify(alarmCommonReduce, times(1)).reduceAndSendAlarm(any(Alert.class));
}
@Test
void addNewAlertReportFromCloud() {
TenCloudAlertReport alertReport = TenCloudAlertReport.builder()
.firstOccurTime("2024-08-01 11:30:00")
.durationTime(100)
.build();
String reportJson = JsonUtil.toJson(alertReport);
assertDoesNotThrow(() -> alertService.addNewAlertReportFromCloud("tencloud", reportJson));
verify(alarmCommonReduce, times(1)).reduceAndSendAlarm(any(Alert.class));
alertService.addNewAlertReportFromCloud("alicloud", reportJson);
reset(alarmCommonReduce);
verify(alarmCommonReduce, times(0)).reduceAndSendAlarm(any(Alert.class));
}
}
@@ -1,113 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.collector.collect.common.cache;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Test case for {@link ConnectionCommonCache}
*/
class CommonCacheTest {
private AbstractConnection<?> mockConnection;
private ConnectionCommonCache<String, AbstractConnection<?>> cache;
@BeforeEach
void setUp() {
cache = new ConnectionCommonCache<>();
mockConnection = new AbstractConnection<>() {
@Override
public Object getConnection() {
return null;
}
@Override
public void closeConnection() throws Exception {
}
};
}
@Test
void testAddAndRetrieveCache() {
String key = "testKey";
cache.addCache(key, mockConnection);
Optional<AbstractConnection<?>> retrieved = cache.getCache(key, false);
assertTrue(retrieved.isPresent());
assertSame(mockConnection, retrieved.get());
}
@Test
void testCacheTimeout() throws InterruptedException {
String key = "timeoutKey";
cache.addCache(key, mockConnection, 1L);
Thread.sleep(2);
Optional<AbstractConnection<?>> retrieved = cache.getCache(key, false);
assertFalse(retrieved.isPresent());
}
@Test
void testRemoveCache() {
String key = "removeKey";
cache.addCache(key, mockConnection);
cache.removeCache(key);
Optional<AbstractConnection<?>> retrieved = cache.getCache(key, false);
assertFalse(retrieved.isPresent());
}
@Test
void testRefreshCache() {
String key = "refreshKey";
cache.addCache(key, mockConnection, 5000L);
Optional<AbstractConnection<?>> firstRetrieval = cache.getCache(key, true);
assertTrue(firstRetrieval.isPresent());
Optional<AbstractConnection<?>> secondRetrieval = cache.getCache(key, false);
assertTrue(secondRetrieval.isPresent());
}
@Test
void testConcurrentAccess() throws InterruptedException {
String key = "concurrentKey";
cache.addCache(key, mockConnection);
Runnable accessCache = () -> {
Optional<AbstractConnection<?>> retrieved = cache.getCache(key, false);
assertTrue(retrieved.isPresent());
};
Thread thread1 = new Thread(accessCache);
Thread thread2 = new Thread(accessCache);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
}
@@ -1,147 +0,0 @@
///*
// * Licensed to the Apache Software Foundation (ASF) under one or more
// * contributor license agreements. See the NOTICE file distributed with
// * this work for additional information regarding copyright ownership.
// * The ASF licenses this file to You under the Apache License, Version 2.0
// * (the "License"); you may not use this file except in compliance with
// * the License. You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//
//package org.apache.hertzbeat.collector.dispatch;
//
//import static org.mockito.ArgumentMatchers.any;
//import static org.mockito.Mockito.doAnswer;
//import static org.mockito.Mockito.mock;
//import static org.mockito.Mockito.times;
//import static org.mockito.Mockito.verify;
//import static org.mockito.Mockito.when;
//import java.util.List;
//import java.util.Set;
//import java.util.concurrent.ExecutorService;
//import java.util.concurrent.Executors;
//import org.apache.hertzbeat.collector.dispatch.entrance.internal.CollectJobService;
//import org.apache.hertzbeat.collector.dispatch.timer.Timeout;
//import org.apache.hertzbeat.collector.dispatch.timer.TimerDispatch;
//import org.apache.hertzbeat.collector.dispatch.timer.WheelTimerTask;
//import org.apache.hertzbeat.common.entity.job.Job;
//import org.apache.hertzbeat.common.entity.job.Metrics;
//import org.apache.hertzbeat.common.entity.message.CollectRep;
//import org.apache.hertzbeat.common.queue.CommonDataQueue;
//import org.junit.jupiter.api.BeforeEach;
//import org.junit.jupiter.api.Test;
//import org.mockito.InjectMocks;
//import org.mockito.Mock;
//import org.mockito.MockitoAnnotations;
//
///**
// * Test case for {@link CommonDispatcher}
// */
//class CommonDispatcherTest {
//
// @Mock
// private MetricsCollectorQueue jobRequestQueue;
//
// @Mock
// private TimerDispatch timerDispatch;
//
// @Mock
// private CommonDataQueue commonDataQueue;
//
// @Mock
// private WorkerPool workerPool;
//
// @InjectMocks
// private CommonDispatcher commonDispatcher;
//
// @Mock
// private CollectJobService collectJobService;
//
// @BeforeEach
// void setUp() {
//
// MockitoAnnotations.openMocks(this);
//
// ExecutorService executorService = Executors.newFixedThreadPool(2);
// doAnswer(invocation -> {
// Runnable task = invocation.getArgument(0);
// executorService.submit(task);
// return null;
// }).when(workerPool).executeJob(any(Runnable.class));
// }
//
// @Test
// void testDispatchMetricsTask() {
//
// Timeout timeout = mock(Timeout.class);
// WheelTimerTask timerTask = mock(WheelTimerTask.class);
//
// Job job = mock(Job.class);
// Set<Metrics> metricsSet = mock(Set.class);
//
// when(timeout.task()).thenReturn(timerTask);
// when(timerTask.getJob()).thenReturn(job);
// when(job.getNextCollectMetrics(null, true)).thenReturn(metricsSet);
//
// commonDispatcher.dispatchMetricsTask(timeout);
//
// verify(job, times(1)).getNextCollectMetrics(null, true);
// }
//
// @Test
// public void testDispatchCollectDataTimeoutMetricsCollectRepMetricsData() {
//
// Timeout timeout = mock(Timeout.class);
// Metrics metrics = mock(Metrics.class);
// WheelTimerTask timerTask = mock(WheelTimerTask.class);
// Job job = mock(Job.class);
// CollectRep.MetricsData metricsData = CollectRep.MetricsData.newBuilder().setMetrics("metrics").build();
//
// when(timeout.task()).thenReturn(timerTask);
// when(timerTask.getJob()).thenReturn(job);
// when(job.getNextCollectMetrics(metrics, false)).thenReturn(null);
// when(timeout.task()).thenReturn(timerTask);
// when(timerTask.getJob()).thenReturn(job);
// when(job.getNextCollectMetrics(metrics, false)).thenReturn(null);
// when(metrics.isHasSubTask()).thenReturn(false);
//
// commonDispatcher.dispatchCollectData(timeout, metrics, metricsData);
//
// verify(commonDataQueue, times(0)).sendMetricsData(metricsData);
// }
//
// @Test
// void testDispatchCollectDataTimeoutMetricsListOfCollectRepMetricsData() {
// Timeout timeout = mock(Timeout.class);
// Metrics metrics = mock(Metrics.class);
// CollectRep.MetricsData metricsData1 = CollectRep.MetricsData.newBuilder().build();
// CollectRep.MetricsData metricsData2 = CollectRep.MetricsData.newBuilder().build();
// List<CollectRep.MetricsData> metricsDataList = List.of(metricsData1, metricsData2);
// WheelTimerTask timerTask = mock(WheelTimerTask.class);
// Job job = mock(Job.class);
//
// when(timeout.task()).thenReturn(timerTask);
// when(timerTask.getJob()).thenReturn(job);
// when(job.isCyclic()).thenReturn(true);
//
// commonDispatcher.dispatchCollectData(timeout, metrics, metricsDataList);
//
// verify(commonDataQueue, times(2)).sendMetricsData(any(CollectRep.MetricsData.class));
// }
//
// @Test
// void testStart() {
//
// commonDispatcher.start();
//
// verify(workerPool, times(2)).executeJob(any(Runnable.class));
// }
//
//}
@@ -1,68 +0,0 @@
///*
// * Licensed to the Apache Software Foundation (ASF) under one or more
// * contributor license agreements. See the NOTICE file distributed with
// * this work for additional information regarding copyright ownership.
// * The ASF licenses this file to You under the Apache License, Version 2.0
// * (the "License"); you may not use this file except in compliance with
// * the License. You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//
//package org.apache.hertzbeat.collector.dispatch;
//
//import static org.junit.jupiter.api.Assertions.assertEquals;
//import static org.junit.jupiter.api.Assertions.assertThrows;
//import static org.mockito.Mockito.mock;
//import java.util.concurrent.locks.ReentrantLock;
//import org.junit.jupiter.api.BeforeEach;
//import org.junit.jupiter.api.Test;
//
///**
// * Test case for {@link MetricsCollectorQueue}
// */
//class MetricsCollectorQueueTest {
//
// private MetricsCollectorQueue metricsCollectorQueue;
//
// private MetricsCollect mockJob;
//
// @BeforeEach
// void setUp() {
//
// metricsCollectorQueue = new MetricsCollectorQueue();
// mockJob = mock(MetricsCollect.class);
// }
//
// @Test
// void testAddJob() throws InterruptedException {
//
// metricsCollectorQueue.addJob(mockJob);
// assertEquals(mockJob, metricsCollectorQueue.getJob());
// }
//
// @Test
// void testGetJobTimeout() {
// ReentrantLock lock = new ReentrantLock();
// Thread run = new Thread(() -> {
// try {
// metricsCollectorQueue.getJob();
// } catch (Exception e) {
// assertThrows(InterruptedException.class, () -> {
// throw e;
// });
// lock.unlock();
// }
// });
// run.start();
// run.interrupt();
// lock.lock();
// }
//
//}
@@ -1,210 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.collector.collect.kafka;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.collector.collect.AbstractCollect;
import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
import org.apache.hertzbeat.common.entity.job.Metrics;
import org.apache.hertzbeat.common.entity.job.protocol.KafkaProtocol;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.DescribeTopicsResult;
import org.apache.kafka.clients.admin.ListTopicsOptions;
import org.apache.kafka.clients.admin.ListTopicsResult;
import org.apache.kafka.clients.admin.OffsetSpec;
import org.apache.kafka.clients.admin.TopicDescription;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.TopicPartitionInfo;
import org.springframework.util.Assert;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@Slf4j
public class KafkaCollectImpl extends AbstractCollect {
@Override
public void preCheck(Metrics metrics) throws IllegalArgumentException {
KafkaProtocol kafkaProtocol = metrics.getKclient();
// Ensure that metrics and kafkaProtocol are not null
Assert.isTrue(metrics != null && kafkaProtocol != null, "Kafka collect must have kafkaProtocol params");
// Ensure that host and port are not empty
Assert.hasText(kafkaProtocol.getHost(), "Kafka Protocol host is required.");
Assert.hasText(kafkaProtocol.getPort(), "Kafka Protocol port is required.");
}
@Override
public void collect(CollectRep.MetricsData.Builder builder, long monitorId, String app, Metrics metrics) {
try {
KafkaProtocol kafkaProtocol = metrics.getKclient();
String command = kafkaProtocol.getCommand();
boolean isKafkaCommand = SupportedCommand.isKafkaCommand(command);
if (!isKafkaCommand) {
log.error("Unsupported command: {}", command);
return;
}
// Create AdminClient with the provided host and port
AdminClient adminClient = KafkaConnect.getAdminClient(kafkaProtocol.getHost() + ":" + kafkaProtocol.getPort());
// Execute the appropriate collection method based on the command
switch (SupportedCommand.fromCommand(command)) {
case TOPIC_DESCRIBE:
collectTopicDescribe(builder, adminClient);
break;
case TOPIC_LIST:
collectTopicList(builder, adminClient);
break;
case TOPIC_OFFSET:
collectTopicOffset(builder, adminClient);
break;
default:
log.error("Unsupported command: {}", command);
break;
}
} catch (InterruptedException | ExecutionException e) {
log.error("Kafka collect error", e);
}
}
/**
* Collect the earliest and latest offsets for each topic
*
* @param builder The MetricsData builder
* @param adminClient The AdminClient
* @throws InterruptedException If the thread is interrupted
* @throws ExecutionException If an error occurs during execution
*/
private void collectTopicOffset(CollectRep.MetricsData.Builder builder, AdminClient adminClient) throws InterruptedException, ExecutionException {
ListTopicsResult listTopicsResult = adminClient.listTopics(new ListTopicsOptions().listInternal(true));
Set<String> names = listTopicsResult.names().get();
names.forEach(name -> {
try {
Map<String, TopicDescription> map = adminClient.describeTopics(Collections.singleton(name)).all().get(3L, TimeUnit.SECONDS);
map.forEach((key, value) -> value.partitions().forEach(info -> extractedOffset(builder, adminClient, name, value, info)));
} catch (TimeoutException | InterruptedException | ExecutionException e) {
log.warn("Topic {} get offset fail", name);
}
});
}
private void extractedOffset(CollectRep.MetricsData.Builder builder, AdminClient adminClient, String name, TopicDescription value, TopicPartitionInfo info) {
try {
TopicPartition topicPartition = new TopicPartition(value.name(), info.partition());
long earliestOffset = getEarliestOffset(adminClient, topicPartition);
long latestOffset = getLatestOffset(adminClient, topicPartition);
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
valueRowBuilder.addColumns(value.name());
valueRowBuilder.addColumns(String.valueOf(info.partition()));
valueRowBuilder.addColumns(String.valueOf(earliestOffset));
valueRowBuilder.addColumns(String.valueOf(latestOffset));
builder.addValues(valueRowBuilder.build());
} catch (TimeoutException | InterruptedException | ExecutionException e) {
log.warn("Topic {} get offset fail", name);
}
}
/**
* Get the earliest offset for a given topic partition
*
* @param adminClient The AdminClient
* @param topicPartition The TopicPartition
* @return The earliest offset
*/
private long getEarliestOffset(AdminClient adminClient, TopicPartition topicPartition)
throws InterruptedException, ExecutionException, TimeoutException {
return adminClient
.listOffsets(Collections.singletonMap(topicPartition, OffsetSpec.earliest()))
.all()
.get(3L, TimeUnit.SECONDS)
.get(topicPartition)
.offset();
}
/**
* Get the latest offset for a given topic partition
*
* @param adminClient The AdminClient
* @param topicPartition The TopicPartition
* @return The latest offset
*/
private long getLatestOffset(AdminClient adminClient, TopicPartition topicPartition)
throws InterruptedException, ExecutionException, TimeoutException {
return adminClient
.listOffsets(Collections.singletonMap(topicPartition, OffsetSpec.latest()))
.all()
.get(3L, TimeUnit.SECONDS)
.get(topicPartition)
.offset();
}
/**
* Collect the list of topics
*
* @param builder The MetricsData builder
* @param adminClient The AdminClient
*/
private static void collectTopicList(CollectRep.MetricsData.Builder builder, AdminClient adminClient) throws InterruptedException, ExecutionException {
ListTopicsOptions options = new ListTopicsOptions().listInternal(true);
Set<String> names = adminClient.listTopics(options).names().get();
names.forEach(name -> {
CollectRep.ValueRow valueRow = CollectRep.ValueRow.newBuilder().addColumns(name).build();
builder.addValues(valueRow);
});
}
/**
* Collect the description of each topic
*
* @param builder The MetricsData builder
* @param adminClient The AdminClient
*/
private static void collectTopicDescribe(CollectRep.MetricsData.Builder builder, AdminClient adminClient) throws InterruptedException, ExecutionException {
ListTopicsOptions options = new ListTopicsOptions();
options.listInternal(true);
ListTopicsResult listTopicsResult = adminClient.listTopics(options);
Set<String> names = listTopicsResult.names().get();
DescribeTopicsResult describeTopicsResult = adminClient.describeTopics(names);
Map<String, TopicDescription> map = describeTopicsResult.all().get();
map.forEach((key, value) -> {
List<TopicPartitionInfo> listp = value.partitions();
listp.forEach(info -> {
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
valueRowBuilder.addColumns(value.name());
valueRowBuilder.addColumns(String.valueOf(value.partitions().size()));
valueRowBuilder.addColumns(String.valueOf(info.partition()));
valueRowBuilder.addColumns(info.leader().host());
valueRowBuilder.addColumns(String.valueOf(info.leader().port()));
valueRowBuilder.addColumns(String.valueOf(info.replicas().size()));
valueRowBuilder.addColumns(String.valueOf(info.replicas()));
builder.addValues(valueRowBuilder.build());
});
});
}
@Override
public String supportProtocol() {
return DispatchConstants.PROTOCOL_KAFKA;
}
}
@@ -1,64 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.collector.collect.kafka;
import org.apache.hertzbeat.collector.collect.common.cache.AbstractConnection;
import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.AdminClientConfig;
import org.apache.kafka.clients.admin.KafkaAdminClient;
import java.util.Properties;
/**
* Kafka connection
*/
public class KafkaConnect extends AbstractConnection<AdminClient> {
private static AdminClient adminClient;
public KafkaConnect(String brokerList) {
Properties properties = new Properties();
properties.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, brokerList);
properties.put(AdminClientConfig.RETRIES_CONFIG, 3);
properties.put(AdminClientConfig.RETRY_BACKOFF_MS_CONFIG, 500);
adminClient = KafkaAdminClient.create(properties);
}
@Override
public AdminClient getConnection() {
return adminClient;
}
@Override
public void closeConnection() throws Exception {
if (this.adminClient != null) {
this.adminClient.close();
}
}
public static synchronized AdminClient getAdminClient(String brokerList) {
if (adminClient == null) {
Properties properties = new Properties();
properties.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, brokerList);
adminClient = KafkaAdminClient.create(properties);
}
return adminClient;
}
}
@@ -1,60 +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.common.cache;
import java.time.Duration;
/**
* common cache factory
*/
public final class CacheFactory {
private CacheFactory() {}
private static final CommonCacheService<String, Object> NOTICE_CACHE =
new CaffeineCacheServiceImpl<>(10, 1000, Duration.ofDays(1), false);
private static final CommonCacheService<String, Object> ALERT_SILENCE_CACHE =
new CaffeineCacheServiceImpl<>(10, 1000, Duration.ofDays(1), false);
private static final CommonCacheService<String, Object> ALERT_CONVERGE_CACHE =
new CaffeineCacheServiceImpl<>(10, 1000, Duration.ofDays(1), false);
/**
* get notice cache
* @return caffeine cache
*/
public static CommonCacheService<String, Object> getNoticeCache() {
return NOTICE_CACHE;
}
/**
* get alert silence cache
* @return caffeine cache
*/
public static CommonCacheService<String, Object> getAlertSilenceCache() {
return ALERT_SILENCE_CACHE;
}
/**
* get alert converge cache
* @return converge cache
*/
public static CommonCacheService<String, Object> getAlertConvergeCache() {
return ALERT_CONVERGE_CACHE;
}
}
@@ -1,158 +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.common.entity.alerter;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_ONLY;
import static io.swagger.v3.oas.annotations.media.Schema.AccessMode.READ_WRITE;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
/**
* Alarm record entity
*/
@Entity
@Table(name = "hzb_alert")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Alarm record entity")
@EntityListeners(AuditingEntityListener.class)
public class Alert {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(title = "Alarm record entity primary key index ID",
description = "Alarm record entity primary key index ID",
example = "87584674384", accessMode = READ_ONLY)
private Long id;
@Schema(title = "Alert target object: monitor availability-available metrics-app.metrics.field",
description = "Alert target object: monitor availability-available metrics-app.metrics.field",
example = "1", accessMode = READ_WRITE)
@Size(max = 255)
private String target;
@Schema(title = "Alarm definition ID associated with the alarm",
description = "Alarm definition ID associated with the alarm",
example = "8743267443543", accessMode = READ_WRITE)
private Long alertDefineId;
@Schema(title = "Alarm level 0:High-Emergency-Critical Alarm 1:Medium-Critical-Critical Alarm 2:Low-Warning-Warning",
example = "1", accessMode = READ_WRITE)
@Min(0)
@Max(2)
private byte priority;
@Schema(title = "The actual content of the alarm notification",
description = "The actual content of the alarm notification",
example = "linux_192.134.32.1: 534543534 cpu usage high",
accessMode = READ_WRITE)
@Column(length = 4096)
private String content;
@Schema(title = "Alarm status: "
+ "0-normal alarm (to be processed) "
+ "1-threshold triggered but not reached the number of alarms "
+ "2-recovered alarm "
+ "3-processed",
description = "Alarm status: "
+ "0-normal alarm (to be processed) "
+ "1-threshold triggered but not reached the number of alarms "
+ "2-recovered alarm "
+ "3-processed",
example = "1", accessMode = READ_WRITE)
@Min(0)
@Max(3)
private byte status;
@Schema(title = "Alarm times",
description = "Alarm times",
example = "3", accessMode = READ_WRITE)
private Integer times;
@Schema(title = "Alarm trigger time (timestamp in milliseconds)",
description = "Alarm trigger time (timestamp in milliseconds)",
example = "1612198922000", accessMode = READ_ONLY)
private Long firstAlarmTime;
@Schema(title = "Alarm trigger time (timestamp in milliseconds)",
description = "Alarm trigger time (timestamp in milliseconds)",
example = "1612198922000", accessMode = READ_ONLY)
private Long lastAlarmTime;
@Schema(title = "Alarm threshold trigger times",
description = "Alarm threshold trigger times",
example = "3", accessMode = READ_WRITE)
@Transient
private Integer triggerTimes;
@Schema(description = "Alarm information label(monitorId:xxx,monitorName:xxx)",
example = "{key1:value1}", accessMode = READ_WRITE)
@Convert(converter = JsonMapAttributeConverter.class)
@SuppressWarnings("JpaAttributeTypeInspection")
@Column(length = 2048)
private Map<String, String> tags;
@Schema(title = "The creator of this record", example = "tom", accessMode = READ_ONLY)
@CreatedBy
private String creator;
@Schema(title = "The modifier of this record", example = "tom", accessMode = READ_ONLY)
@LastModifiedBy
private String modifier;
@Schema(title = "Record the latest creation time (timestamp in milliseconds)",
description = "Record the latest creation time (timestamp in milliseconds)",
example = "1612198922000", accessMode = READ_ONLY)
@CreatedDate
private LocalDateTime gmtCreate;
@Schema(title = "Record modify time", example = "1612198444000", accessMode = READ_ONLY)
@LastModifiedDate
private LocalDateTime gmtUpdate;
@Override
public Alert clone() {
// deep clone
return JsonUtil.fromJson(JsonUtil.toJson(this), Alert.class);
}
}
@@ -1,58 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.entity.manager;
import com.fasterxml.jackson.core.type.TypeReference;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.springframework.stereotype.Component;
/**
* json str to tag list
*/
@Converter
@Component
public class JsonTagListAttributeConverter implements AttributeConverter<List<TagItem>, String> {
@Override
public String convertToDatabaseColumn(List<TagItem> attribute) {
return JsonUtil.toJson(attribute);
}
@Override
public List<TagItem> convertToEntityAttribute(String dbData) {
TypeReference<List<TagItem>> typeReference = new TypeReference<>() {};
List<TagItem> tagItems = JsonUtil.fromJson(dbData, typeReference);
if (tagItems == null) {
TypeReference<Map<String, String>> mapTypeReference = new TypeReference<>() {};
Map<String, String> map = JsonUtil.fromJson(dbData, mapTypeReference);
if (map != null) {
return map.entrySet().stream().map(entry -> new TagItem(entry.getKey(), entry.getValue())).collect(Collectors.toList());
} else {
return null;
}
} else {
return tagItems;
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,53 +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.common.serialize;
import java.util.Map;
import org.apache.hertzbeat.common.entity.alerter.Alert;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.apache.kafka.common.header.Headers;
import org.apache.kafka.common.serialization.Serializer;
/**
* kafka alert entity serializer
*/
public class AlertSerializer implements Serializer<Alert> {
@Override
public void configure(Map<String, ?> configs, boolean isKey) {
Serializer.super.configure(configs, isKey);
}
@Override
public byte[] serialize(String s, Alert alert) {
if (alert == null){
return null;
}
return JsonUtil.toJson(alert).getBytes();
}
@Override
public byte[] serialize(String topic, Headers headers, Alert data) {
return Serializer.super.serialize(topic, headers, data);
}
@Override
public void close() {
Serializer.super.close();
}
}
@@ -1,255 +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.common.util.prometheus;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* prometheus metric sparser
*/
public final class PrometheusUtil {
//An unknown format occurred during parsing because parsing cannot continue
// or the end of the input stream has been reached
private static final int ERROR_FORMAT = -1;
//The input stream ends normally
private static final int NORMAL_END = -2;
private static final int COMMENT_LINE = -3;
private PrometheusUtil() {
}
private static int parseMetricName(InputStream inputStream, Metric.MetricBuilder metricBuilder) throws IOException {
StringBuilder stringBuilder = new StringBuilder();
int i;
i = inputStream.read();
if (i == -1) {
return NORMAL_END;
}
else if (i == '#') {
return COMMENT_LINE;
}
while (i != -1) {
if (i == ' ' || i == '{') {
metricBuilder.metricName(stringBuilder.toString());
return i;
}
stringBuilder.append((char) i);
i = inputStream.read();
}
return ERROR_FORMAT;
}
private static int parseLabel(InputStream inputStream, List<Label> labelList) throws IOException {
Label.LabelBuilder labelBuilder = new Label.LabelBuilder();
int i;
StringBuilder labelName = new StringBuilder();
i = inputStream.read();
while (i != -1 && i != '=') {
labelName.append((char) i);
i = inputStream.read();
}
if (i == -1) {
return ERROR_FORMAT;
}
labelBuilder.name(labelName.toString());
if (inputStream.read() != '\"') {
return ERROR_FORMAT;
}
StringBuilder labelValue = new StringBuilder();
i = inputStream.read();
while (i != -1 && i != ',' && i != '}') {
labelValue.append((char) i);
i = inputStream.read();
}
if (i == -1 || labelValue.charAt(labelValue.length() - 1) != '\"') {
return ERROR_FORMAT;
}
// skip space only in this condition
if (i == '}' && inputStream.read() != ' ') {
return ERROR_FORMAT;
}
labelValue.deleteCharAt(labelValue.length() - 1);
labelBuilder.value(labelValue.toString());
labelList.add(labelBuilder.build());
return i;
}
private static int parseLabelList(InputStream inputStream, Metric.MetricBuilder metricBuilder) throws IOException {
List<Label> labelList = new ArrayList<>();
int i;
i = parseLabel(inputStream, labelList);
while (i == ',') {
i = parseLabel(inputStream, labelList);
}
if (i == -1) {
return ERROR_FORMAT;
}
metricBuilder.labelList(labelList);
return i;
}
private static int parseValue(InputStream inputStream, Metric.MetricBuilder metricBuilder) throws IOException {
int i;
StringBuilder stringBuilder = new StringBuilder();
i = inputStream.read();
while (i != -1 && i != ' ' && i != '\n') {
stringBuilder.append((char) i);
i = inputStream.read();
}
String string = stringBuilder.toString();
switch (string) {
case "NaN":
metricBuilder.value(Double.NaN);
break;
case "+Inf":
metricBuilder.value(Double.POSITIVE_INFINITY);
break;
case "-Inf":
metricBuilder.value(Double.NEGATIVE_INFINITY);
break;
default:
try {
BigDecimal bigDecimal = new BigDecimal(string);
metricBuilder.value(bigDecimal.doubleValue());
} catch (NumberFormatException e) {
return ERROR_FORMAT;
}
break;
}
if (i == -1) {
return NORMAL_END;
}
else {
return i; // ' ' or \n'
}
}
private static int parseTimestamp(InputStream inputStream, Metric.MetricBuilder metricBuilder) throws IOException {
int i;
StringBuilder stringBuilder = new StringBuilder();
i = inputStream.read();
while (i != -1 && i != '\n') {
stringBuilder.append((char) i);
i = inputStream.read();
}
String string = stringBuilder.toString();
try {
metricBuilder.timestamp(Long.parseLong(string));
} catch (NumberFormatException e) {
return ERROR_FORMAT;
}
if (i == -1) {
return NORMAL_END;
}
else {
return i; // '\n'
}
}
// return value:
// -1: error format
// -2: normal end
// '\n': more lines
private static int parseMetric(InputStream inputStream, List<Metric> metrics) throws IOException {
Metric.MetricBuilder metricBuilder = new Metric.MetricBuilder();
int i = parseMetricName(inputStream, metricBuilder); // RET: -1, -2, -3, '{', ' '
if (i == ERROR_FORMAT || i == NORMAL_END || i == COMMENT_LINE) {
return i;
}
if (i == '{') {
i = parseLabelList(inputStream, metricBuilder); // RET: -1, '}'
if (i == ERROR_FORMAT) {
return i;
}
}
i = parseValue(inputStream, metricBuilder); // RET: -1, -2, '\n', ' '
if (i != ' ') {
metrics.add(metricBuilder.build());
return i;
}
i = parseTimestamp(inputStream, metricBuilder); // RET: -1, -2, '\n'
metrics.add(metricBuilder.build());
return i;
}
private static int skipCommentLine(InputStream inputStream) throws IOException {
int i = inputStream.read();
while (i != -1 && i != '\n') {
i = inputStream.read();
}
if (i == -1) {
return NORMAL_END;
}
return i;
}
public static List<Metric> parseMetrics(InputStream inputStream) throws IOException {
List<Metric> metricList = new ArrayList<>();
int i = parseMetric(inputStream, metricList);
while (i == '\n' || i == COMMENT_LINE) {
if (i == COMMENT_LINE) {
if (skipCommentLine(inputStream) == NORMAL_END) {
return metricList;
}
}
i = parseMetric(inputStream, metricList);
}
if (i == NORMAL_END) {
return metricList;
}
else {
return null;
}
}
}
-77
View File
@@ -1,77 +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.
*/
syntax = "proto3";
package org.apache.hertzbeat.common.entity.message;
message MetricsData
{
// monitoring id
uint64 id = 1;
// tenant id
uint64 tenantId = 2;
// monitoring app eg: linux | mysql | jvm
string app = 3;
// monitoring metrics eg: cpu | memory | health
string metrics = 4;
// monitoring collect priority >=0
uint32 priority = 5;
// collect timestamp
uint64 time = 6;
// collect response code
Code code = 7;
// collect response error message
string msg = 8;
// monitoring collect metric field
repeated Field fields = 9;
// monitoring collect metric data, mapping with the fields
repeated ValueRow values = 10;
}
message Field
{
// monitoring collect metric field name
string name = 1;
// monitoring collect metrics field type, 0-number 1-string
uint32 type = 2;
// monitoring collect metrics field unit, % MB GB TB S...
string unit = 3;
// is label field
bool label = 4;
}
message ValueRow
{
// monitoring collect metrics value, mapping with the fields
repeated string columns = 1;
}
enum Code
{
// collect success
SUCCESS = 0;
// collector not available
UN_AVAILABLE = 1;
// peer network un reachable(icmp)
UN_REACHABLE = 2;
// peer network server un connectable(tcp,udp...)
UN_CONNECTABLE = 3;
// collect metrics data failed(http,ssh,snmp...)
FAIL = 4;
// collect metrics data timeout
TIMEOUT = 5;
}
@@ -1,172 +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.common.queue.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.lang.reflect.Field;
import java.time.Duration;
import java.util.Collections;
import org.apache.hertzbeat.common.config.CommonProperties;
import org.apache.hertzbeat.common.entity.alerter.Alert;
import org.apache.hertzbeat.common.entity.message.CollectRep;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.TopicPartition;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
/**
* Test case for {@link KafkaCommonDataQueue}
*/
class KafkaCommonDataQueueTest {
private KafkaProducer<Long, CollectRep.MetricsData> metricsDataProducer;
private KafkaProducer<Long, Alert> alertDataProducer;
private KafkaConsumer<Long, Alert> alertDataConsumer;
private KafkaConsumer<Long, CollectRep.MetricsData> metricsDataToAlertConsumer;
private KafkaConsumer<Long, CollectRep.MetricsData> metricsDataToPersistentStorageConsumer;
private KafkaConsumer<Long, CollectRep.MetricsData> metricsDataToRealTimeStorageConsumer;
private CommonProperties.KafkaProperties kafkaProperties;
private KafkaCommonDataQueue kafkaCommonDataQueue;
@BeforeEach
void setUp() throws Exception {
kafkaProperties = mock(CommonProperties.KafkaProperties.class);
when(kafkaProperties.getServers()).thenReturn("localhost:9092");
when(kafkaProperties.getAlertsDataTopic()).thenReturn("alerts");
when(kafkaProperties.getMetricsDataTopic()).thenReturn("metrics");
CommonProperties properties = mock(CommonProperties.class);
CommonProperties.DataQueueProperties queueProperties = mock(CommonProperties.DataQueueProperties.class);
when(properties.getQueue()).thenReturn(queueProperties);
when(queueProperties.getKafka()).thenReturn(kafkaProperties);
metricsDataProducer = mock(KafkaProducer.class);
alertDataProducer = mock(KafkaProducer.class);
alertDataConsumer = mock(KafkaConsumer.class);
metricsDataToAlertConsumer = mock(KafkaConsumer.class);
metricsDataToPersistentStorageConsumer = mock(KafkaConsumer.class);
metricsDataToRealTimeStorageConsumer = mock(KafkaConsumer.class);
kafkaCommonDataQueue = new KafkaCommonDataQueue(properties);
setPrivateField(kafkaCommonDataQueue, "metricsDataProducer", metricsDataProducer);
setPrivateField(kafkaCommonDataQueue, "alertDataProducer", alertDataProducer);
setPrivateField(kafkaCommonDataQueue, "alertDataConsumer", alertDataConsumer);
setPrivateField(kafkaCommonDataQueue, "metricsDataToAlertConsumer", metricsDataToAlertConsumer);
setPrivateField(kafkaCommonDataQueue, "metricsDataToPersistentStorageConsumer", metricsDataToPersistentStorageConsumer);
setPrivateField(kafkaCommonDataQueue, "metricsDataToRealTimeStorageConsumer", metricsDataToRealTimeStorageConsumer);
}
// Test use, set private field.
private void setPrivateField(Object target, String fieldName, Object value) throws Exception {
Field field = target.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(target, value);
}
@Test
void testSendAlertsData() {
Alert alert = new Alert();
kafkaCommonDataQueue.sendAlertsData(alert);
ArgumentCaptor<ProducerRecord<Long, Alert>> captor = ArgumentCaptor.forClass(ProducerRecord.class);
verify(alertDataProducer).send(captor.capture());
ProducerRecord<Long, Alert> record = captor.getValue();
assertEquals("alerts", record.topic());
assertEquals(alert, record.value());
}
@Test
void testPollAlertsData() throws InterruptedException {
Alert alert = new Alert();
ConsumerRecords<Long, Alert> records = new ConsumerRecords<>(Collections.emptyMap());
when(alertDataConsumer.poll(Duration.ofSeconds(1))).thenReturn(records);
assertNull(kafkaCommonDataQueue.pollAlertsData());
records = new ConsumerRecords<>(Collections.singletonMap(
new TopicPartition("alerts", 0),
Collections.singletonList(
new ConsumerRecord<>("alerts", 0, 0L, 1L, alert)
)
));
when(alertDataConsumer.poll(Duration.ofSeconds(1))).thenReturn(records);
assertEquals(alert, kafkaCommonDataQueue.pollAlertsData());
}
@Test
void testSendMetricsData() {
CollectRep.MetricsData metricsData = CollectRep.MetricsData.newBuilder().build();
kafkaCommonDataQueue.sendMetricsData(metricsData);
ArgumentCaptor<ProducerRecord<Long, CollectRep.MetricsData>> captor = ArgumentCaptor.forClass(ProducerRecord.class);
verify(metricsDataProducer).send(captor.capture());
ProducerRecord<Long, CollectRep.MetricsData> record = captor.getValue();
assertEquals("metrics", record.topic());
assertEquals(metricsData, record.value());
}
@Test
void testPollMetricsDataToAlerter() throws InterruptedException {
CollectRep.MetricsData metricsData = CollectRep.MetricsData.newBuilder().build();
ConsumerRecords<Long, CollectRep.MetricsData> records = new ConsumerRecords<>(Collections.emptyMap());
when(metricsDataToAlertConsumer.poll(Duration.ofSeconds(1))).thenReturn(records);
assertNull(kafkaCommonDataQueue.pollMetricsDataToAlerter());
records = new ConsumerRecords<>(Collections.singletonMap(
new TopicPartition("metrics", 0),
Collections.singletonList(
new ConsumerRecord<>("metrics", 0, 0L, 1L, metricsData)
)
));
when(metricsDataToAlertConsumer.poll(Duration.ofSeconds(1))).thenReturn(records);
assertEquals(metricsData, kafkaCommonDataQueue.pollMetricsDataToAlerter());
}
@Test
void testDestroy() throws Exception {
kafkaCommonDataQueue.destroy();
verify(metricsDataProducer).close();
verify(alertDataProducer).close();
verify(alertDataConsumer).close();
verify(metricsDataToAlertConsumer).close();
verify(metricsDataToPersistentStorageConsumer).close();
verify(metricsDataToRealTimeStorageConsumer).close();
}
}
@@ -1,96 +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.common.serialize;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Map;
import org.apache.hertzbeat.common.entity.alerter.Alert;
import org.apache.kafka.common.header.Headers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* test case for {@link AlertDeserializer}
*/
class AlertDeserializerTest {
private AlertDeserializer alertDeserializer;
@Mock
private Map<String, ?> configs;
@Mock
private Headers headers;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
alertDeserializer = new AlertDeserializer();
}
@Test
void testConfigure() {
alertDeserializer.configure(configs, false);
}
@Test
void testDeserializeWithBytes() {
String json = "{\"target\":\"test\",\"content\":\"test\"}";
byte[] bytes = json.getBytes();
Alert expectedAlert = Alert.builder()
.content("test")
.target("test")
.build();
Alert actualAlert = alertDeserializer.deserialize("", bytes);
assertEquals(expectedAlert.getContent(), actualAlert.getContent());
assertEquals(expectedAlert.getTarget(), actualAlert.getTarget());
}
@Test
void testDeserializeWithHeaders() {
String topic = "alerts";
byte[] data = "{\"target\":\"test\",\"content\":\"test\"}".getBytes();
Alert expectedAlert = Alert.builder()
.content("test")
.target("test")
.build();
Alert actualAlert = alertDeserializer.deserialize(topic, headers, data);
assertEquals(expectedAlert.getContent(), actualAlert.getContent());
assertEquals(expectedAlert.getTarget(), actualAlert.getTarget());
}
@Test
void testClose() {
alertDeserializer.close();
}
}
@@ -1,106 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.common.serialize;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.util.Arrays;
import java.util.Map;
import org.apache.hertzbeat.common.entity.alerter.Alert;
import org.apache.kafka.common.header.Headers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* test case for {@link AlertSerializer}
*/
class AlertSerializerTest {
private AlertSerializer alertSerializer;
@Mock
private Map<String, ?> configs;
@Mock
private Headers headers;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
alertSerializer = new AlertSerializer();
}
@Test
void testConfigure() {
alertSerializer.configure(configs, false);
}
@Test
void testSerializeWithAlert() {
Alert alert = Alert.builder()
.content("test")
.target("test")
.build();
byte[] expectedJson = ("{\"id\":null,\"target\":\"test\",\"alertDefineId\":null,\"priority\":0,\"content\":"
+ "\"test\",\"status\":0,\"times\":null,\"firstAlarmTime\":null,\"lastAlarmTime\":null,\"triggerTimes"
+ "\":null,\"tags\":null,\"creator\":null,\"modifier\":null,\"gmtCreate\":null,\"gmtUpdate\":null}").getBytes();
byte[] bytes = alertSerializer.serialize("", alert);
assertNotNull(bytes);
assertEquals(Arrays.toString(expectedJson), Arrays.toString(bytes));
}
@Test
void testSerializeWithNullAlert() {
byte[] bytes = alertSerializer.serialize("", null);
assertNull(bytes);
}
@Test
void testSerializeWithHeaders() {
Alert alert = Alert.builder()
.content("test")
.target("test")
.build();
byte[] expectedBytes = ("{\"id\":null,\"target\":\"test\",\"alertDefineId\":null,\"priority\":0,\"content\":"
+ "\"test\",\"status\":0,\"times\":null,\"firstAlarmTime\":null,\"lastAlarmTime\":null,\"triggerTimes"
+ "\":null,\"tags\":null,\"creator\":null,\"modifier\":null,\"gmtCreate\":null,\"gmtUpdate\":null}").getBytes();
byte[] bytes = alertSerializer.serialize("alerts", headers, alert);
assertArrayEquals(expectedBytes, bytes);
}
@Test
void testClose() {
alertSerializer.close();
}
}
@@ -1,225 +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.common.util.prometheus;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.junit.jupiter.api.Test;
/**
* test case for {@link PrometheusUtil}
*/
class PrometheusUtilTest {
@Test
void testParseMetricsNormalInput() throws IOException {
String input = "metric_name{label1=\"value1\",label2=\"value2\"} 123.45 67890\n";
InputStream inputStream = new ByteArrayInputStream(input.getBytes());
List<Metric> metrics = PrometheusUtil.parseMetrics(inputStream);
assertNotNull(metrics);
assertEquals(1, metrics.size());
Metric metric = metrics.get(0);
assertEquals("metric_name", metric.getMetricName());
assertEquals(123.45, metric.getValue());
assertEquals(67890, metric.getTimestamp());
List<Label> labels = metric.getLabelList();
assertEquals(2, labels.size());
assertEquals("label1", labels.get(0).getName());
assertEquals("value1", labels.get(0).getValue());
assertEquals("label2", labels.get(1).getName());
assertEquals("value2", labels.get(1).getValue());
}
@Test
void testParseMetricsComments() throws IOException {
String input = "# This is a comment\nmetric_name{label1=\"value1\"} 123.45 67890\n";
InputStream inputStream = new ByteArrayInputStream(input.getBytes());
List<Metric> metrics = PrometheusUtil.parseMetrics(inputStream);
assertNotNull(metrics);
assertEquals(1, metrics.size());
Metric metric = metrics.get(0);
assertEquals("metric_name", metric.getMetricName());
assertEquals(123.45, metric.getValue());
assertEquals(67890, metric.getTimestamp());
List<Label> labels = metric.getLabelList();
assertEquals(1, labels.size());
assertEquals("label1", labels.get(0).getName());
assertEquals("value1", labels.get(0).getValue());
}
@Test
void testParseMetricsMalformedInput() throws IOException {
String input = "metric_name{label1=value1} 123.45 67890\n";
InputStream inputStream = new ByteArrayInputStream(input.getBytes());
List<Metric> metrics = PrometheusUtil.parseMetrics(inputStream);
assertNull(metrics);
}
@Test
void testParseMetricsNanValue() throws IOException {
String input = "metric_name{label1=\"value1\"} NaN 67890\n";
InputStream inputStream = new ByteArrayInputStream(input.getBytes());
List<Metric> metrics = PrometheusUtil.parseMetrics(inputStream);
assertNotNull(metrics);
assertEquals(1, metrics.size());
Metric metric = metrics.get(0);
assertEquals("metric_name", metric.getMetricName());
assertTrue(Double.isNaN(metric.getValue()));
assertEquals(67890, metric.getTimestamp());
}
@Test
void testParseMetricsPositiveInfinity() throws IOException {
String input = "metric_name{label1=\"value1\"} +Inf 67890\n";
InputStream inputStream = new ByteArrayInputStream(input.getBytes());
List<Metric> metrics = PrometheusUtil.parseMetrics(inputStream);
assertNotNull(metrics);
assertEquals(1, metrics.size());
Metric metric = metrics.get(0);
assertEquals("metric_name", metric.getMetricName());
assertEquals(Double.POSITIVE_INFINITY, metric.getValue());
assertEquals(67890, metric.getTimestamp());
}
@Test
void testParseMetricsNegativeInfinity() throws IOException {
String input = "metric_name{label1=\"value1\"} -Inf 67890\n";
InputStream inputStream = new ByteArrayInputStream(input.getBytes());
List<Metric> metrics = PrometheusUtil.parseMetrics(inputStream);
assertNotNull(metrics);
assertEquals(1, metrics.size());
Metric metric = metrics.get(0);
assertEquals("metric_name", metric.getMetricName());
assertEquals(Double.NEGATIVE_INFINITY, metric.getValue());
assertEquals(67890, metric.getTimestamp());
}
@Test
void testParseMetricsWithoutLabels() throws IOException {
String input = "metric_name 123.45 67890\n";
InputStream inputStream = new ByteArrayInputStream(input.getBytes());
List<Metric> metrics = PrometheusUtil.parseMetrics(inputStream);
assertNotNull(metrics);
assertEquals(1, metrics.size());
Metric metric = metrics.get(0);
assertEquals("metric_name", metric.getMetricName());
assertEquals(123.45, metric.getValue());
assertEquals(67890, metric.getTimestamp());
assertNull(metric.getLabelList());
}
@Test
void testParseMetricsWithoutTimestamp() throws IOException {
String input = "metric_name{label1=\"value1\"} 123.45\n";
InputStream inputStream = new ByteArrayInputStream(input.getBytes());
List<Metric> metrics = PrometheusUtil.parseMetrics(inputStream);
assertNotNull(metrics);
assertEquals(1, metrics.size());
Metric metric = metrics.get(0);
assertEquals("metric_name", metric.getMetricName());
assertEquals(123.45, metric.getValue());
assertEquals(null, metric.getTimestamp()); // Assuming 0 for no timestamp
List<Label> labels = metric.getLabelList();
assertEquals(1, labels.size());
assertEquals("label1", labels.get(0).getName());
assertEquals("value1", labels.get(0).getValue());
}
@Test
void testParseMultipleMetrics() throws IOException {
String input = "metric_name1{label1=\"value1\"} 123.45 67890\nmetric_name2 678.90 12345\n";
InputStream inputStream = new ByteArrayInputStream(input.getBytes());
List<Metric> metrics = PrometheusUtil.parseMetrics(inputStream);
assertNotNull(metrics);
assertEquals(2, metrics.size());
Metric metric1 = metrics.get(0);
assertEquals("metric_name1", metric1.getMetricName());
assertEquals(123.45, metric1.getValue());
assertEquals(67890, metric1.getTimestamp());
List<Label> labels1 = metric1.getLabelList();
assertEquals(1, labels1.size());
assertEquals("label1", labels1.get(0).getName());
assertEquals("value1", labels1.get(0).getValue());
Metric metric2 = metrics.get(1);
assertEquals("metric_name2", metric2.getMetricName());
assertEquals(678.90, metric2.getValue());
assertEquals(12345, metric2.getTimestamp());
assertNull(metric2.getLabelList());
}
@Test
void testParseEmptyInput() throws IOException {
String input = "";
InputStream inputStream = new ByteArrayInputStream(input.getBytes());
List<Metric> metrics = PrometheusUtil.parseMetrics(inputStream);
assertNotNull(metrics);
assertTrue(metrics.isEmpty());
}
}
+5 -5
View File
@@ -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
+59 -1
View File
@@ -32,9 +32,14 @@
<!-- 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>
<!-- spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -50,6 +55,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 +93,44 @@
<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>
</dependency>
<dependency>
<groupId>org.apache.arrow</groupId>
<artifactId>arrow-memory-netty</artifactId>
</dependency>
</dependencies>
</project>
@@ -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)
@@ -0,0 +1,106 @@
/*
* 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 lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.hertzbeat.alert.service.DataSourceService;
import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
import org.apache.hertzbeat.common.util.JexlExpressionRunner;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.collections4.CollectionUtils;
/**
* Periodic Alert Calculator
*/
@Slf4j
@RequiredArgsConstructor
public class PeriodicAlertCalculator {
private final DataSourceService dataSourceService;
private final JexlExpressionRunner expressionRunner;
private final Map<String, SingleAlert> notRecoveredAlertMap = new ConcurrentHashMap<>(16);
public List<SingleAlert> calculate(AlertDefine rule) {
if (!rule.isEnable() || StringUtils.isEmpty(rule.getExpr())) {
return Collections.emptyList();
}
// todo: implement the following logic
try {
// Execute query
List<Map<String, Object>> queryResults = dataSourceService.query(
rule.getDatasource(),
rule.getExpr()
);
if (CollectionUtils.isEmpty(queryResults)) {
return Collections.emptyList();
}
// Execute expression calculation on query results
List<SingleAlert> newAlerts = queryResults.stream()
.filter(result -> execAlertExpression(result, rule.getExpr()))
.map(result -> buildAlert(rule, result))
.collect(Collectors.toList());
// Handle recovery notification
if (newAlerts.isEmpty()) {
return handleAlertRecover(rule);
}
return newAlerts;
} catch (Exception e) {
log.error("Calculate periodic rule {} failed: {}", rule.getName(), e.getMessage());
return Collections.emptyList();
}
}
private boolean execAlertExpression(Map<String, Object> result, String expr) {
return false;
}
private SingleAlert buildAlert(AlertDefine rule, Map<String, Object> metrics) {
return SingleAlert.builder()
.labels(rule.getLabels())
.annotations(rule.getAnnotations())
.triggerTimes(1)
.startAt(System.currentTimeMillis())
.activeAt(System.currentTimeMillis())
.build();
}
private List<SingleAlert> handleAlertRecover(AlertDefine rule) {
SingleAlert firingAlert = notRecoveredAlertMap.remove(rule.getId().toString());
if (firingAlert != null) {
return Collections.singletonList(buildResolvedAlert(rule, firingAlert));
}
return Collections.emptyList();
}
private SingleAlert buildResolvedAlert(AlertDefine rule, SingleAlert firingAlert) {
return null;
}
}
@@ -0,0 +1,29 @@
/*
* 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 lombok.extern.slf4j.Slf4j;
/**
* period alert rule scheduler
*/
@Slf4j
public class PeriodicAlertRuleScheduler {
// todo implement the following logic
}
@@ -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]));
}
}
@@ -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));
}
}
@@ -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));
}
@@ -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));
}
}
@@ -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());
}
@@ -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 Silence management API
*/
@Tag(name = "Alert Silence API")
@RestController
@RequestMapping(path = "/api/alert/inhibit", produces = {APPLICATION_JSON_VALUE})
public class AlertInhibitController {
@Autowired
private AlertInhibitService alertInhibitService;
@PostMapping
@Operation(summary = "New Alarm Silence", description = "Added an alarm Silence")
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 Silence", description = "Modify an existing alarm Silence")
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 Silence",
description = "You can obtain alarm Silence information based on the alarm Silence ID")
public ResponseEntity<Message<AlertInhibit>> getAlertInhibit(
@Parameter(description = "Alarm Silence 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));
}
}
@@ -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;
/**
* Silence the batch API for alarms
*/
@Tag(name = "Alert Silence 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 Silence ID", example = "6565463543") @RequestParam(required = false) List<Long> ids,
@Parameter(description = "Search Name", example = "x") @RequestParam(required = false) String search,
@Parameter(description = "Sort field, default id", example = "id") @RequestParam(defaultValue = "id") String sort,
@Parameter(description = "Sort mode: asc: ascending, desc: descending", example = "desc") @RequestParam(defaultValue = "desc") String order,
@Parameter(description = "List current page", example = "0") @RequestParam(defaultValue = "0") int pageIndex,
@Parameter(description = "Number of list pages", example = "8") @RequestParam(defaultValue = "8") int pageSize) {
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 Silence 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);
}
}
@@ -0,0 +1,115 @@
/*
* 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 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.service.ExternAlertService;
import org.apache.hertzbeat.common.constants.CommonConstants;
import org.apache.hertzbeat.common.entity.dto.Message;
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.RestController;
/**
* Extern Alarm Manage API
*/
@Tag(name = "Extern Alarm Manage API")
@RestController
@Slf4j
public class AlertReportController {
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"));
}
}
@@ -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);
@@ -15,21 +15,21 @@
* 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.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -87,6 +87,18 @@ public class NoticeConfigController {
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeReceivers(name)));
}
@GetMapping(path = "/receiver/{id}")
@Operation(summary = "Get the recipient information based on the recipient ID",
description = "Get the recipient information based on the recipient ID")
public ResponseEntity<Message<NoticeReceiver>> getReceiverById(
@Parameter(description = "en: Recipient ID", example = "6565463543") @PathVariable("id") final Long receiverId) {
NoticeReceiver noticeReceiver = noticeConfigService.getReceiverById(receiverId);
if (noticeReceiver == null) {
return ResponseEntity.ok(Message.fail(FAIL_CODE, "The relevant information of the recipient could not be found, please check whether the parameters are correct or refresh the page"));
}
return ResponseEntity.ok(Message.success(noticeReceiver));
}
@PostMapping(path = "/rule")
@Operation(summary = "Add a notification policy", description = "Add a notification policy")
public ResponseEntity<Message<Void>> addNewNoticeRule(@Valid @RequestBody NoticeRule noticeRule) {
@@ -122,6 +134,17 @@ public class NoticeConfigController {
return ResponseEntity.ok(Message.success(noticeConfigService.getNoticeRules(name)));
}
@GetMapping(path = "/rule/{id}")
@Operation(summary = "Get the notification policy information based on the policy ID",
description = "Get the notification policy information based on the policy ID")
public ResponseEntity<Message<NoticeRule>> getRuleById(
@Parameter(description = "en: Notification Policy ID", example = "6565463543") @PathVariable("id") final Long ruleId) {
NoticeRule noticeRule = noticeConfigService.getNoticeRulesById(ruleId);
if (noticeRule == null) {
return ResponseEntity.ok(Message.fail(FAIL_CODE, "The specified notification rule could not be queried, please check whether the parameters are correct or refresh the page"));
}
return ResponseEntity.ok(Message.success(noticeRule));
}
@PostMapping(path = "/template")
@Operation(summary = "Add a notification template", description = "Add a notification template")
@@ -159,6 +182,18 @@ public class NoticeConfigController {
return ResponseEntity.ok(Message.success(templatePage));
}
@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")
public ResponseEntity<Message<NoticeTemplate>> getTemplateById(
@Parameter(description = "en: Notification template ID", example = "6565463543") @PathVariable("id") final Long templateId) {
Optional<NoticeTemplate> noticeTemplate = noticeConfigService.getNoticeTemplatesById(templateId);
if (noticeTemplate.isEmpty()) {
return ResponseEntity.ok(Message.fail(FAIL_CODE, "The specified notification template could not be queried, please check whether the parameters are correct or refresh the page"));
}
return ResponseEntity.ok(Message.success(noticeTemplate.get()));
}
@PostMapping(path = "/receiver/send-test-msg")
@Operation(summary = "Send test msg to receiver", description = "Send test msg to receiver")
public ResponseEntity<Message<Void>> sendTestMsg(@Valid @RequestBody NoticeReceiver noticeReceiver) {
@@ -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);
}
@@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hertzbeat.alert.dao;
import java.util.List;
import java.util.Set;
import org.apache.hertzbeat.common.entity.alerter.AlertGroupConverge;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
/**
* AlertConverge Dao
*/
public interface AlertGroupConvergeDao extends JpaRepository<AlertGroupConverge, Long>, JpaSpecificationExecutor<AlertGroupConverge> {
/**
* Delete group alarm converge based on the ID list
* @param ids alert converge id list
*/
@Modifying
void deleteAlertGroupConvergesByIdIn(Set<Long> ids);
/**
* Query the enable true group alarm converge list
* @return group alarm converge list
*/
List<AlertGroupConverge> findAlertGroupConvergesByEnableIsTrue();
}
@@ -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();
}
@@ -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);
}
@@ -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;
@@ -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;
@@ -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);
}
@@ -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;
}
@@ -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;
}
@@ -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;
@@ -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.dto;
import java.time.Instant;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* Prometheus Alert Content Entity
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PrometheusExternAlert {
private Map<String, String> labels;
private Map<String, String> annotations;
private String status;
private Instant startsAt;
private Instant endsAt;
private String generatorURL;
}
@@ -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";
}
@@ -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,14 @@ 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.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.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,24 +38,19 @@ 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;
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) {
this.workerPool = workerPool;
this.dataQueue = dataQueue;
this.noticeConfigService = noticeConfigService;
this.alertStoreHandler = alertStoreHandler;
this.pluginRunner = pluginRunner;
@@ -67,15 +58,6 @@ public class DispatcherAlarm implements InitializingBean {
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 +65,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 +97,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
alertStoreHandler.store(groupAlert);
// Notice distribution
sendNotify(groupAlert);
// Execute the plugin if enable (Compatible with old version plugins, will be removed in later versions)
pluginRunner.pluginExecute(Plugin.class, plugin -> plugin.alert(groupAlert));
// Execute the plugin if enable with params
pluginRunner.pluginExecute(PostAlertPlugin.class, (afterAlertPlugin, pluginContext) -> afterAlertPlugin.execute(groupAlert, pluginContext));
}
}
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());
}
}));
}));
}
}
@@ -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
@@ -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
@@ -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
@@ -30,5 +30,5 @@ public interface AlertStoreHandler {
* to the alert tag information tags while persisting.
* @param alert alarm information
*/
void store(Alert alert);
void store(GroupAlert alert);
}
@@ -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());
@@ -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());
@@ -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;
@@ -0,0 +1,125 @@
/*
* 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.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 void store(GroupAlert groupAlert) {
if (groupAlert == null || groupAlert.getAlerts() == null || groupAlert.getAlerts().isEmpty()) {
log.error("The Group Alerts is empty, ignore store");
return;
}
// 1. Find existing alert group
GroupAlert existGroupAlert = groupAlertDao.findByGroupKey(groupAlert.getGroupKey());
// 2. Process individual alerts
Set<String> alertFingerprints = new HashSet<>(8);
groupAlert.getAlerts().forEach(singleAlert -> {
SingleAlert existAlert = singleAlertDao.findByFingerprint(singleAlert.getFingerprint());
if (existAlert != null) {
// Update existing alert
singleAlert.setId(existAlert.getId());
singleAlert.setGmtCreate(existAlert.getGmtCreate());
// Status transition logic
if (CommonConstants.ALERT_STATUS_FIRING.equals(singleAlert.getStatus())) {
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())) {
// Transition to resolved state
if (singleAlert.getEndAt() == null) {
singleAlert.setEndAt(System.currentTimeMillis());
}
singleAlert.setStartAt(existAlert.getStartAt());
singleAlert.setActiveAt(existAlert.getActiveAt());
singleAlert.setTriggerTimes(existAlert.getTriggerTimes());
}
}
alertFingerprints.add(singleAlert.getFingerprint());
singleAlertDao.save(singleAlert);
});
// 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());
groupAlertDao.save(groupAlert);
}
}
@@ -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();
@@ -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;
@@ -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());
@@ -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);
@@ -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"));
@@ -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()
@@ -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"));
@@ -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))
@@ -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());
@@ -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;
@@ -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);
@@ -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 {
/**
@@ -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;
}

Some files were not shown because too many files have changed in this diff Show More