├── .gitignore ├── LICENSE ├── README.md ├── README_kr.md ├── pom.xml └── src ├── main └── java │ └── scouter │ └── plugin │ └── server │ └── alert │ └── slack │ ├── Message.java │ ├── MonitoringGroupConfigure.java │ └── SlackPlugin.java └── test └── java └── scouter └── plugin └── server └── alert └── slack └── MonitoringGroupConfigureTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | .DS_Store 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.war 8 | *.ear 9 | 10 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 11 | hs_err_pid* 12 | /out/ 13 | 14 | *.iml 15 | target/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scouter-plugin-server-alert-slack 2 | ### Scouter server plugin to send a alert via slack 3 | 4 | - this project inspired by telegram plugin project of noces96. it is very similar. 5 | 6 | - this project is scouter server plugin project. this project goal is that send message to slack. 7 | - this project only support a sort of Alert. 8 | - CPU of Agent (warning / fatal) 9 | - Memory of Agent (warning / fatal) 10 | - Disk of Agent (warning / fatal) 11 | - connected new Agent 12 | - disconnected Agent 13 | - reconnect Agent 14 | 15 | ### Properties (you can modify in conf/scouter.conf of scouter server home ) 16 | * **_ext\_plugin\_slack\_send\_alert_** : can send slack message or can'not (true / false) - default : false 17 | * **_ext\_plugin\_slack\_debug_** : can log message or can't - default false 18 | * **_ext\_plugin\_slack\_level_** : log level (0 : INFO, 1 : WARN, 2 : ERROR, 3 : FATAL) - default 0 19 | * **_ext\_plugin\_slack\_webhook_url_** : Slack WebHook URL 20 | * **_ext\_plugin\_slack\_channel_** : #Channel or @user_id 21 | * **_ext\_plugin\_slack\_botName_** : bot name 22 | * **_ext\_plugin\_slack\_icon\_emoji_** : Slack emoticon (if it is existed slack emotion, it will use slack emotion ) 23 | * **_ext\_plugin\_slack\_icon\_url_** : icon image URL 24 | * **_ext\_plugin\_elapsed\_time\_threshold_** : 응답시간의 임계치 (ms) - 기본 값은 0으로, 0일때 응답시간의 임계치 초과 여부를 확인하지 않는다. 25 | * **_ext\_plugin\_gc\_time\_threshold_** : GC Time의 임계치 (ms) - 기본 값은 0으로, 0일때 GC Time의 임계치 초과 여부를 확인하지 않는다. 26 | * **_ext\_plugin\_thread\_count\_threshold_** : Thread Count의 임계치 - 기본 값은 0으로, 0일때 Thread Count의 임계치 초과 여부를 확인하지 않는다. 27 | * **_ext\_plugin\_slack\_xlog\_enabled_** : xlog message send (true / false) - default : false 28 | * **_ext_plugin_slack_object_alert_enabled_** : object active/dead alert (true / false) - default : false 29 | 30 | * Example 31 | ``` 32 | # External Interface (Slack) 33 | ext_plugin_slack_send_alert=true 34 | ext_plugin_slack_debug=true 35 | ext_plugin_slack_level=1 36 | ext_plugin_slack_webhook_url=https://hooks.slack.com/services/T02XXXXX/B159XXXXX/W5CDXXXXXXXXXXXXXXXXXXXX 37 | ext_plugin_slack_channel=#scouter 38 | ext_plugin_slack_botName=scouter 39 | ext_plugin_slack_icon_emoji=:computer: 40 | ext_plugin_slack_icon_url=http://XXX.XXX.XXX/XXX.gif 41 | ext_plugin_slack_xlog_enabled=true 42 | ext_plugin_slack_object_alert_enabled=true 43 | 44 | ext_plugin_elapsed_time_threshold=5000 45 | ext_plugin_gc_time_threshold=5000 46 | ext_plugin_thread_count_threshold=300 47 | ``` 48 | 49 | ### Dependencies 50 | * Project 51 | - scouter.common 52 | - scouter.server 53 | * Library 54 | - commons-codec-1.9.jar 55 | - commons-logging-1.2.jar 56 | - gson-2.6.2.jar 57 | - httpclient-4.5.2.jar 58 | - httpcore-4.4.4.jar 59 | 60 | ### Build & Deploy 61 | * Pre-condition 62 | - should set scouter server home in .bashrc or .zshrc 63 | * Build 64 | - ant compile 65 | 66 | * Deploy 67 | - ant dist 68 | -------------------------------------------------------------------------------- /README_kr.md: -------------------------------------------------------------------------------- 1 | # scouter-plugin-server-alert-slack 2 | ### Scouter server plugin to send a alert via slack 3 | 4 | - noces96님의 telegram plugin project를 토대로 만들었습니다. 매우 흡사합니다 ^^ 5 | 6 | - 본 프로젝트는 스카우터 서버 플러그인으로써 서버에서 발생한 Alert 메시지를 Slack 으로 전송하는 역할을 한다. 7 | - 현재 지원되는 Alert의 종류는 다음과 같다. 8 | - Agent의 CPU (warning / fatal) 9 | - Agent의 Memory (warning / fatal) 10 | - Agent의 Disk (warning / fatal) 11 | - 신규 Agent 연결 12 | - Agent의 연결 해제 13 | - Agent의 재접속 14 | 15 | ### Properties (스카우터 서버 설치 경로 하위의 conf/scouter.conf) 16 | * **_ext\_plugin\_slack\_send\_alert_** : Slack 메시지 발송 여부 (true / false) - 기본 값은 false 17 | * **_ext\_plugin\_slack\_debug_** : 로깅 여부 - 기본 값은 false 18 | * **_ext\_plugin\_slack\_level_** : 수신 레벨(0 : INFO, 1 : WARN, 2 : ERROR, 3 : FATAL) - 기본 값은 0 19 | * **_ext\_plugin\_slack\_webhook_url_** : Slack WebHook URL 20 | * **_ext\_plugin\_slack\_channel_** : 채널(#Channel) 혹은 아이디(@user_id) 21 | * **_ext\_plugin\_slack\_botName_** : bot name 22 | * **_ext\_plugin\_slack\_icon\_emoji_** : Slack 이모티콘 이름 (icon url 보다 우선순위 ) 23 | * **_ext\_plugin\_slack\_icon\_url_** : icon 이미지 URL 24 | * **_ext\_plugin\_elapsed\_time\_threshold_** : 응답시간의 임계치 (ms) - 기본 값은 0으로, 0일때 응답시간의 임계치 초과 여부를 확인하지 않는다. 25 | * **_ext\_plugin\_gc\_time\_threshold_** : GC Time의 임계치 (ms) - 기본 값은 0으로, 0일때 GC Time의 임계치 초과 여부를 확인하지 않는다. 26 | * **_ext\_plugin\_thread\_count\_threshold_** : Thread Count의 임계치 - 기본 값은 0으로, 0일때 Thread Count의 임계치 초과 여부를 확인하지 않는다. 27 | * **_ext\_plugin\_slack\_xlog\_enabled_** : xlog maasege send (true / false) - default : false 28 | * **_ext_plugin_slack_object_alert_enabled_** : object active/dead alert (true / false) - default : false 29 | 30 | * Example 31 | ``` 32 | # External Interface (Slack) 33 | ext_plugin_slack_send_alert=true 34 | ext_plugin_slack_debug=true 35 | ext_plugin_slack_level=1 36 | ext_plugin_slack_webhook_url=https://hooks.slack.com/services/T02XXXXX/B159XXXXX/W5CDXXXXXXXXXXXXXXXXXXXX 37 | ext_plugin_slack_channel=#scouter 38 | ext_plugin_slack_botName=scouter 39 | ext_plugin_slack_icon_emoji=:computer: 40 | ext_plugin_slack_icon_url=http://XXX.XXX.XXX/XXX.gif 41 | ext_plugin_slack_xlog_enabled=true 42 | 43 | ext_plugin_elapsed_time_threshold=5000 44 | ext_plugin_gc_time_threshold=5000 45 | ext_plugin_thread_count_threshold=300 46 | ``` 47 | 48 | ### Dependencies 49 | * Project 50 | - scouter.common 51 | - scouter.server 52 | * Library 53 | - commons-codec-1.9.jar 54 | - commons-logging-1.2.jar 55 | - gson-2.6.2.jar 56 | - httpclient-4.5.2.jar 57 | - httpcore-4.4.4.jar 58 | 59 | ### Build & Deploy 60 | * Pre-condition 61 | - .bashrc 나 .zshrc에 SCOUTER_SERVER_HOME 변수를 설정해야합니다 62 | 63 | * Build 64 | - ant compile 65 | 66 | * Deploy 67 | - ant dist 68 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 22 | 4.0.0 23 | 24 | com.github.scouter-project 25 | scouter-plugin-server-alert-slack 26 | 1.1.0 27 | 28 | 29 | UTF-8 30 | 31 | 32 | 33 | 34 | io.github.scouter-project 35 | scouter-common 36 | 1.8.3 37 | provided 38 | 39 | 40 | io.github.scouter-project 41 | scouter-server 42 | 1.8.3 43 | provided 44 | 45 | 46 | 47 | org.apache.httpcomponents 48 | httpclient 49 | 4.5.13 50 | 51 | 52 | 53 | org.apache.httpcomponents 54 | httpcore 55 | 4.4.4 56 | 57 | 58 | 59 | com.google.code.gson 60 | gson 61 | 2.6.2 62 | 63 | 64 | 65 | commons-logging 66 | commons-logging 67 | 1.2 68 | 69 | 70 | 71 | commons-codec 72 | commons-codec 73 | 1.9 74 | 75 | 76 | junit 77 | junit 78 | 4.13.1 79 | test 80 | 81 | 82 | org.assertj 83 | assertj-core 84 | 2.3.0 85 | test 86 | 87 | 88 | org.mockito 89 | mockito-all 90 | 1.10.8 91 | test 92 | 93 | 94 | 95 | 96 | 97 | 98 | org.apache.maven.plugins 99 | maven-compiler-plugin 100 | 3.1 101 | 102 | 1.8 103 | 1.8 104 | 105 | 106 | 107 | org.apache.maven.plugins 108 | maven-dependency-plugin 109 | 110 | 111 | copy-dependencies 112 | prepare-package 113 | 114 | copy-dependencies 115 | 116 | 117 | ${project.build.directory}/lib 118 | false 119 | false 120 | true 121 | runtime 122 | 123 | 124 | 125 | 126 | 127 | org.apache.maven.plugins 128 | maven-source-plugin 129 | 130 | 131 | attach-sources 132 | 133 | jar 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /src/main/java/scouter/plugin/server/alert/slack/Message.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Scouter Project. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * @author Se-Wang Lee 17 | */ 18 | package scouter.plugin.server.alert.slack; 19 | 20 | import com.google.gson.annotations.SerializedName; 21 | 22 | /** 23 | * Alert message class to send alert via Slack 24 | * 25 | * @author Se-Wang Lee(ssamzie101@gmail.com) on 2016. 5. 2. 26 | */ 27 | public class Message { 28 | 29 | @SerializedName("text") 30 | private String text; 31 | @SerializedName("channel") 32 | private String channel; 33 | @SerializedName("username") 34 | private String botName; 35 | @SerializedName("icon_emoji") 36 | private String iconEmoji; 37 | @SerializedName("icon_url") 38 | private String iconURL; 39 | 40 | public Message(String text, String channel, String botName, String iconURL, String iconEmoji){ 41 | this.text = text; 42 | this.channel = channel; 43 | this.botName = botName; 44 | this.iconURL = iconURL; 45 | this.iconEmoji = iconEmoji; 46 | } 47 | 48 | public String getText() { 49 | return text; 50 | } 51 | public void setText(String text) { 52 | this.text = text; 53 | } 54 | public String getChannel() { 55 | return channel; 56 | } 57 | public void setChannel(String channel) { 58 | this.channel = channel; 59 | } 60 | public String getBotName() { 61 | return botName; 62 | } 63 | public void setBotName(String botName) { 64 | this.botName = botName; 65 | } 66 | public String getIconURL() { 67 | return iconURL; 68 | } 69 | public void setIconURL(String iconURL) { 70 | this.iconURL = iconURL; 71 | } 72 | 73 | public String getIconEmoji() { 74 | return iconEmoji; 75 | } 76 | 77 | public void setIconEmoji(String iconEmoji) { 78 | this.iconEmoji = iconEmoji; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/scouter/plugin/server/alert/slack/MonitoringGroupConfigure.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Scouter Project. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * @author Gookeun Lim 17 | */ 18 | package scouter.plugin.server.alert.slack; 19 | 20 | import scouter.server.Configure; 21 | 22 | /** 23 | *

a class that helps to find a property value which matches to a specific monitoring_group_type key(aks obj_type). 24 | * * You can defined a montioring_group_type values like below

25 | * 26 | * {objType}.{remain value}ext_plugin_slack_... 27 | * 28 | * ex) 29 | * ext_plugin_slack_channel=general_monitoring 30 | * order_jvm.ext_plugin_slack_channel=order_monitoring 31 | * stock_jvm.ext_plugin_slack_channel=stock_monitoring 32 | * 33 | * @author Gookeun Lim (passion.lim@gmail.com) on 2018.07.24 34 | * 35 | */ 36 | public class MonitoringGroupConfigure { 37 | private Configure conf; 38 | 39 | public MonitoringGroupConfigure(Configure config) { 40 | this.conf = config; 41 | } 42 | 43 | public String getValue(String key, String objType) { 44 | return this.getValue(key, objType, null); 45 | } 46 | 47 | public String getGroupKey(String originalKey, String objType) { 48 | if (originalKey == null) { 49 | return originalKey; 50 | } 51 | 52 | return objType+"."+originalKey; 53 | } 54 | 55 | public String getValue(String key, String objType, String defaultValue) { 56 | String groupKey = getGroupKey(key, objType); 57 | String value = conf.getValue(groupKey); 58 | if (value != null && value.trim().length() > 0) { 59 | return value; 60 | } 61 | // default key value 62 | value = conf.getValue(key); 63 | return value != null? value : defaultValue; 64 | } 65 | 66 | public Boolean getBoolean(String key, final String objType, Boolean defaultValue) { 67 | String groupKey = getGroupKey(key, objType); 68 | Boolean value = toBoolean(conf.getValue(groupKey)); 69 | if (value != null) { 70 | return value; 71 | } 72 | // default key value 73 | value = toBoolean(conf.getValue(key)); 74 | return value != null? value : defaultValue; 75 | } 76 | 77 | public int getInt(String key, String objType, int defaultValue) { 78 | String groupKey = getGroupKey(key, objType); 79 | Integer value = toInteger(conf.getValue(groupKey)); 80 | if (value != null) { 81 | return value; 82 | } 83 | // default key value 84 | value = toInteger(conf.getValue(key)); 85 | return value != null ? value : defaultValue; 86 | } 87 | 88 | public long getLong(String key, String objType, long defaultValue) { 89 | String groupKey = getGroupKey(key, objType); 90 | Long value = toLong(conf.getValue(groupKey)); 91 | if (value != null) { 92 | return value; 93 | } 94 | // default key value 95 | value = toLong(conf.getValue(key)); 96 | return value != null ? value : defaultValue; 97 | } 98 | 99 | 100 | private Long toLong(String value) { 101 | try { 102 | if (value != null) { 103 | return Long.parseLong(value); 104 | } 105 | } catch (Exception e) { 106 | // ignore exception 107 | } 108 | return null; 109 | } 110 | 111 | private Integer toInteger(String value) { 112 | try { 113 | if (value != null) { 114 | return Integer.parseInt(value); 115 | } 116 | } catch (Exception e) { 117 | // ignore exception 118 | } 119 | return null; 120 | } 121 | 122 | private Boolean toBoolean(String value) { 123 | try { 124 | if (value != null) { 125 | return Boolean.parseBoolean(value); 126 | } 127 | } catch (Exception e) { 128 | // ignore exception 129 | } 130 | return null; 131 | } 132 | } -------------------------------------------------------------------------------- /src/main/java/scouter/plugin/server/alert/slack/SlackPlugin.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright 2016 Scouter Project. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | * @author Se-Wang Lee 18 | */ 19 | package scouter.plugin.server.alert.slack; 20 | 21 | import com.google.gson.Gson; 22 | import org.apache.http.HttpResponse; 23 | import org.apache.http.HttpStatus; 24 | import org.apache.http.client.methods.HttpPost; 25 | import org.apache.http.entity.StringEntity; 26 | import org.apache.http.impl.client.CloseableHttpClient; 27 | import org.apache.http.impl.client.HttpClientBuilder; 28 | import org.apache.http.util.EntityUtils; 29 | import scouter.lang.AlertLevel; 30 | import scouter.lang.TextTypes; 31 | import scouter.lang.TimeTypeEnum; 32 | import scouter.lang.counters.CounterConstants; 33 | import scouter.lang.pack.AlertPack; 34 | import scouter.lang.pack.MapPack; 35 | import scouter.lang.pack.ObjectPack; 36 | import scouter.lang.pack.PerfCounterPack; 37 | import scouter.lang.pack.XLogPack; 38 | import scouter.lang.plugin.PluginConstants; 39 | import scouter.lang.plugin.annotation.ServerPlugin; 40 | import scouter.net.RequestCmd; 41 | import scouter.server.Configure; 42 | import scouter.server.CounterManager; 43 | import scouter.server.Logger; 44 | import scouter.server.core.AgentManager; 45 | import scouter.server.db.TextRD; 46 | import scouter.server.netio.AgentCall; 47 | import scouter.util.DateUtil; 48 | import scouter.util.HashUtil; 49 | 50 | import java.util.ArrayList; 51 | import java.util.List; 52 | import java.util.concurrent.Executors; 53 | import java.util.concurrent.ScheduledExecutorService; 54 | import java.util.concurrent.TimeUnit; 55 | import java.util.concurrent.atomic.AtomicInteger; 56 | 57 | /** 58 | * Scouter server plugin to send alert via Slack 59 | * 60 | * @author Se-Wang Lee(ssamzie101@gmail.com) on 2016. 5. 2. 61 | */ 62 | public class SlackPlugin { 63 | 64 | final Configure conf = Configure.getInstance(); 65 | 66 | private final MonitoringGroupConfigure groupConf; 67 | 68 | private static AtomicInteger ai = new AtomicInteger(0); 69 | private static List javaeeObjHashList = new ArrayList(); 70 | 71 | public SlackPlugin() { 72 | groupConf = new MonitoringGroupConfigure(conf); 73 | 74 | if (ai.incrementAndGet() == 1) { 75 | ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); 76 | 77 | // thread count check 78 | executor.scheduleAtFixedRate(new Runnable() { 79 | @Override 80 | public void run() { 81 | if (conf.getInt("ext_plugin_thread_count_threshold", 0) == 0) { 82 | return; 83 | } 84 | for (int objHash : javaeeObjHashList) { 85 | try { 86 | if (AgentManager.isActive(objHash)) { 87 | ObjectPack objectPack = AgentManager.getAgent(objHash); 88 | MapPack mapPack = new MapPack(); 89 | mapPack.put("objHash", objHash); 90 | 91 | mapPack = AgentCall.call(objectPack, RequestCmd.OBJECT_THREAD_LIST, mapPack); 92 | 93 | int threadCountThreshold = groupConf.getInt("ext_plugin_thread_count_threshold", objectPack.objType, 0); 94 | int threadCount = mapPack.getList("name").size(); 95 | 96 | if (threadCountThreshold != 0 && threadCount > threadCountThreshold) { 97 | AlertPack ap = new AlertPack(); 98 | 99 | ap.level = AlertLevel.WARN; 100 | ap.objHash = objHash; 101 | ap.title = "Thread count exceed a threshold."; 102 | ap.message = objectPack.objName + "'s Thread count(" + threadCount + ") exceed a threshold."; 103 | ap.time = System.currentTimeMillis(); 104 | ap.objType = objectPack.objType; 105 | 106 | alert(ap); 107 | } 108 | } 109 | } catch (Exception e) { 110 | // ignore 111 | } 112 | } 113 | } 114 | }, 115 | 0, 5, TimeUnit.SECONDS); 116 | } 117 | } 118 | 119 | @ServerPlugin(PluginConstants.PLUGIN_SERVER_ALERT) 120 | public void alert(final AlertPack pack){ 121 | if (groupConf.getBoolean("ext_plugin_slack_send_alert", pack.objType, false)) { 122 | int level = groupConf.getInt("ext_plugin_slack_level", pack.objType, 0); 123 | // Get log level (0 : INFO, 1 : WARN, 2 : ERROR, 3 : FATAL) 124 | if(level <= pack.level){ 125 | new Thread(){ 126 | public void run(){ 127 | try{ 128 | String webhookURL = groupConf.getValue("ext_plugin_slack_webhook_url", pack.objType); 129 | String channel = groupConf.getValue("ext_plugin_slack_channel", pack.objType); 130 | String botName = groupConf.getValue("ext_plugin_slack_botName", pack.objType); 131 | String iconURL = groupConf.getValue("ext_plugin_slack_icon_url", pack.objType); 132 | String iconEmoji = groupConf.getValue("ext_plugin_slack_icon_emoji", pack.objType); 133 | 134 | assert webhookURL != null; 135 | 136 | // Get the agent Name 137 | String name = AgentManager.getAgentName(pack.objHash) == null ? "N/A" : AgentManager.getAgentName(pack.objHash); 138 | 139 | if (name.equals("N/A") && pack.message.endsWith("connected.")) { 140 | int idx = pack.message.indexOf("connected"); 141 | if (pack.message.indexOf("reconnected") > -1) { 142 | name = pack.message.substring(0, idx - 6); 143 | } else { 144 | name = pack.message.substring(0, idx - 4); 145 | } 146 | } 147 | 148 | String title = pack.title; 149 | String msg = pack.message; 150 | if (title.equals("INACTIVE_OBJECT")) { 151 | title = "An object has been inactivated."; 152 | msg = pack.message.substring(0, pack.message.indexOf("OBJECT") - 1); 153 | } 154 | 155 | // Make message contents 156 | String contents = "[TYPE] : " + pack.objType.toUpperCase() + "\n" + 157 | "[NAME] : " + name + "\n" + 158 | "[LEVEL] : " + AlertLevel.getName(pack.level) + "\n" + 159 | "[TITLE] : " + title + "\n" + 160 | "[MESSAGE] : " + msg; 161 | 162 | Message message = new Message(contents, channel, botName, iconURL, iconEmoji); 163 | String payload = new Gson().toJson(message); 164 | 165 | if(groupConf.getBoolean("ext_plugin_slack_debug", pack.objType, false)){ 166 | println("WebHookURL : "+webhookURL); 167 | println("param : "+payload); 168 | } 169 | 170 | HttpPost post = new HttpPost(webhookURL); 171 | post.addHeader("Content-Type","application/json"); 172 | // charset set utf-8 173 | post.setEntity(new StringEntity(payload, "utf-8")); 174 | 175 | CloseableHttpClient client = HttpClientBuilder.create().build(); 176 | 177 | // send the post request 178 | HttpResponse response = client.execute(post); 179 | 180 | if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { 181 | println("Slack message sent to [" + channel + "] successfully."); 182 | } else { 183 | println("Slack message sent failed. Verify below information."); 184 | println("[WebHookURL] : " + webhookURL); 185 | println("[Message] : " + payload); 186 | println("[Reason] : " + EntityUtils.toString(response.getEntity(), "UTF-8")); 187 | } 188 | }catch(Exception e){ 189 | println("[Error] : " + e.getMessage()); 190 | if(conf._trace) { 191 | e.printStackTrace(); 192 | } 193 | } 194 | } 195 | 196 | }.start(); 197 | } 198 | } 199 | } 200 | 201 | @ServerPlugin(PluginConstants.PLUGIN_SERVER_OBJECT) 202 | public void object(ObjectPack pack){ 203 | if (!conf.getBoolean("ext_plugin_slack_object_alert_enabled", false)) { 204 | return; 205 | } 206 | 207 | if (pack.version != null && pack.version.length() > 0) { 208 | AlertPack ap = null; 209 | ObjectPack op = AgentManager.getAgent(pack.objHash); 210 | 211 | if (op == null && pack.wakeup == 0L) { 212 | // in case of new agent connected 213 | ap = new AlertPack(); 214 | ap.level = AlertLevel.INFO; 215 | ap.objHash = pack.objHash; 216 | ap.title = "An object has been activated."; 217 | ap.message = pack.objName + " is connected."; 218 | ap.time = System.currentTimeMillis(); 219 | 220 | if (AgentManager.getAgent(pack.objHash) != null) { 221 | ap.objType = AgentManager.getAgent(pack.objHash).objType; 222 | } else { 223 | ap.objType = "scouter"; 224 | } 225 | 226 | alert(ap); 227 | } else if (op.alive == false) { 228 | // in case of agent reconnected 229 | ap = new AlertPack(); 230 | ap.level = AlertLevel.INFO; 231 | ap.objHash = pack.objHash; 232 | ap.title = "An object has been activated."; 233 | ap.message = pack.objName + " is reconnected."; 234 | ap.time = System.currentTimeMillis(); 235 | ap.objType = AgentManager.getAgent(pack.objHash).objType; 236 | 237 | alert(ap); 238 | } 239 | // inactive state can be handled in alert() method. 240 | } 241 | } 242 | 243 | @ServerPlugin(PluginConstants.PLUGIN_SERVER_XLOG) 244 | public void xlog(XLogPack pack) { 245 | if (!conf.getBoolean("ext_plugin_slack_xlog_enabled", false)) { 246 | return; 247 | } 248 | 249 | String objType = AgentManager.getAgent(pack.objHash).objType; 250 | if (groupConf.getBoolean("ext_plugin_slack_xlog_enabled", objType, true)) { 251 | if (pack.error != 0) { 252 | String date = DateUtil.yyyymmdd(pack.endTime); 253 | String service = TextRD.getString(date, TextTypes.SERVICE, pack.service); 254 | AlertPack ap = new AlertPack(); 255 | ap.level = AlertLevel.ERROR; 256 | ap.objHash = pack.objHash; 257 | ap.title = "xlog Error"; 258 | ap.message = service + " - " + TextRD.getString(date, TextTypes.ERROR, pack.error); 259 | ap.time = System.currentTimeMillis(); 260 | ap.objType = objType; 261 | alert(ap); 262 | } 263 | 264 | try { 265 | int elapsedThreshold = groupConf.getInt("ext_plugin_elapsed_time_threshold", objType, 0); 266 | 267 | if (elapsedThreshold != 0 && pack.elapsed > elapsedThreshold) { 268 | String serviceName = TextRD.getString(DateUtil.yyyymmdd(pack.endTime), TextTypes.SERVICE, pack.service); 269 | 270 | AlertPack ap = new AlertPack(); 271 | 272 | ap.level = AlertLevel.WARN; 273 | ap.objHash = pack.objHash; 274 | ap.title = "Elapsed time exceed a threshold."; 275 | ap.message = "[" + AgentManager.getAgentName(pack.objHash) + "] " 276 | + pack.service + "(" + serviceName + ") " 277 | + "elapsed time(" + pack.elapsed + " ms) exceed a threshold."; 278 | ap.time = System.currentTimeMillis(); 279 | ap.objType = objType; 280 | 281 | alert(ap); 282 | } 283 | 284 | } catch (Exception e) { 285 | Logger.printStackTrace(e); 286 | } 287 | } 288 | } 289 | 290 | 291 | @ServerPlugin(PluginConstants.PLUGIN_SERVER_COUNTER) 292 | public void counter(PerfCounterPack pack) { 293 | String objName = pack.objName; 294 | int objHash = HashUtil.hash(objName); 295 | String objType = null; 296 | String objFamily = null; 297 | 298 | if (AgentManager.getAgent(objHash) != null) { 299 | objType = AgentManager.getAgent(objHash).objType; 300 | } 301 | 302 | if (objType != null) { 303 | objFamily = CounterManager.getInstance().getCounterEngine().getObjectType(objType).getFamily().getName(); 304 | } 305 | 306 | try { 307 | // in case of objFamily is javaee 308 | if (CounterConstants.FAMILY_JAVAEE.equals(objFamily)) { 309 | // save javaee type's objHash 310 | if (!javaeeObjHashList.contains(objHash)) { 311 | javaeeObjHashList.add(objHash); 312 | } 313 | 314 | if (pack.timetype == TimeTypeEnum.REALTIME) { 315 | long gcTimeThreshold = groupConf.getLong("ext_plugin_gc_time_threshold", objType, 0); 316 | long gcTime = pack.data.getLong(CounterConstants.JAVA_GC_TIME); 317 | 318 | if (gcTimeThreshold != 0 && gcTime > gcTimeThreshold) { 319 | AlertPack ap = new AlertPack(); 320 | 321 | ap.level = AlertLevel.WARN; 322 | ap.objHash = objHash; 323 | ap.title = "GC time exceed a threshold."; 324 | ap.message = objName + "'s GC time(" + gcTime + " ms) exceed a threshold."; 325 | ap.time = System.currentTimeMillis(); 326 | ap.objType = objType; 327 | 328 | alert(ap); 329 | } 330 | } 331 | } 332 | } catch (Exception e) { 333 | Logger.printStackTrace(e); 334 | } 335 | } 336 | 337 | private void println(Object o){ 338 | if(conf.getBoolean("ext_plugin_slack_debug", false)){ 339 | System.out.println(o); 340 | Logger.println(o); 341 | } 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /src/test/java/scouter/plugin/server/alert/slack/MonitoringGroupConfigureTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Scouter Project. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * @author Gookeun Lim 17 | */ 18 | package scouter.plugin.server.alert.slack; 19 | 20 | import static org.junit.Assert.assertEquals; 21 | import static org.mockito.Mockito.mock; 22 | import static org.mockito.Mockito.when; 23 | 24 | import org.junit.Test; 25 | 26 | import scouter.server.Configure; 27 | 28 | /** 29 | * Unit Test For MonitoringGroupConfigure 30 | * 31 | * @author Gookeun Lim (passion.lim@gmail.com) on 2018.07.24 32 | */ 33 | public class MonitoringGroupConfigureTest { 34 | 35 | MonitoringGroupConfigure sut; 36 | 37 | @Test 38 | public void test_getValue() { 39 | String objType = "order_jvm"; 40 | Configure conf = mock(Configure.class); 41 | sut = new MonitoringGroupConfigure(conf); 42 | 43 | // default value 44 | assertEquals("http://webhook.example.com/default", sut.getValue("ext_plugin_slack_webhook_url", objType, "http://webhook.example.com/default")); 45 | 46 | // conf value 47 | when(conf.getValue("ext_plugin_slack_webhook_url")).thenReturn("http://webhook.example.com/tomcat"); 48 | assertEquals("http://webhook.example.com/tomcat", sut.getValue("ext_plugin_slack_webhook_url", objType)); 49 | 50 | // appended value with groupKey 51 | when(conf.getValue(objType+".ext_plugin_slack_webhook_url")).thenReturn("http://webhook.example.com/order_jvm"); 52 | assertEquals("http://webhook.example.com/order_jvm", sut.getValue("ext_plugin_slack_webhook_url", objType)); 53 | } 54 | 55 | @Test 56 | public void test_getIntegerValue() { 57 | String objType = "order_jvm"; 58 | Configure conf = mock(Configure.class); 59 | sut = new MonitoringGroupConfigure(conf); 60 | 61 | // default value 62 | assertEquals(1000, sut.getInt("ext_plugin_slack_elapsed_time_threshold", objType, 1000)); 63 | 64 | // conf value 65 | when(conf.getValue("ext_plugin_slack_elapsed_time_threshold")).thenReturn("2000"); 66 | assertEquals(2000, sut.getInt("ext_plugin_slack_elapsed_time_threshold", objType, 1000)); 67 | 68 | // appended value with groupKey 69 | when(conf.getValue(objType+".ext_plugin_slack_elapsed_time_threshold")).thenReturn("3000"); 70 | assertEquals(3000, sut.getInt("ext_plugin_slack_elapsed_time_threshold", objType, 1000)); 71 | 72 | } 73 | 74 | @Test 75 | public void test_getLongValue() { 76 | String objType = "order_jvm"; 77 | Configure conf = mock(Configure.class); 78 | sut = new MonitoringGroupConfigure(conf); 79 | 80 | // default value 81 | assertEquals(1000000000000L, sut.getLong("ext_plugin_slack_elapsed_time_threshold", objType, 1000000000000L)); 82 | 83 | // conf value 84 | when(conf.getValue("ext_plugin_slack_elapsed_time_threshold")).thenReturn("2000000000000"); 85 | assertEquals(2000000000000L, sut.getLong("ext_plugin_slack_elapsed_time_threshold", objType, 1000000000000L)); 86 | 87 | // appended value with groupKey 88 | when(conf.getValue(objType+".ext_plugin_slack_elapsed_time_threshold")).thenReturn("3000000000000"); 89 | assertEquals(3000000000000L, sut.getLong("ext_plugin_slack_elapsed_time_threshold", objType, 1000000000000L)); 90 | } 91 | 92 | @Test 93 | public void test_getBooleanValue() { 94 | String objType = "order_jvm"; 95 | Configure conf = mock(Configure.class); 96 | sut = new MonitoringGroupConfigure(conf); 97 | 98 | // default value 99 | assertEquals(Boolean.TRUE, sut.getBoolean("ext_plugin_slack_elapsed_time_threshold", objType, Boolean.TRUE)); 100 | 101 | // conf value 102 | when(conf.getValue("ext_plugin_slack_elapsed_time_threshold")).thenReturn("false"); 103 | assertEquals(Boolean.FALSE, sut.getBoolean("ext_plugin_slack_elapsed_time_threshold", objType, Boolean.TRUE)); 104 | 105 | // appended value with groupKey 106 | when(conf.getValue(objType+".ext_plugin_slack_elapsed_time_threshold")).thenReturn("true"); 107 | assertEquals(Boolean.TRUE, sut.getBoolean("ext_plugin_slack_elapsed_time_threshold", objType, Boolean.FALSE)); 108 | } 109 | 110 | } 111 | --------------------------------------------------------------------------------