├── .gitignore
├── LICENSE
├── README.md
├── api
├── log4j-api
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── itkevin
│ │ │ └── log4j
│ │ │ └── api
│ │ │ ├── config
│ │ │ └── Log4jConfig.java
│ │ │ ├── filter
│ │ │ └── Log4jFilter.java
│ │ │ └── listener
│ │ │ └── Log4jApplicationListener.java
│ │ └── resources
│ │ └── META-INF
│ │ ├── dubbo
│ │ └── org.apache.dubbo.rpc.Filter
│ │ └── spring.factories
├── logback-api
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── itkevin
│ │ │ └── logback
│ │ │ └── api
│ │ │ ├── config
│ │ │ └── LogbackConfig.java
│ │ │ ├── filter
│ │ │ └── LogbackFilter.java
│ │ │ └── listener
│ │ │ └── LogbackApplicationListener.java
│ │ └── resources
│ │ └── META-INF
│ │ ├── dubbo
│ │ └── org.apache.dubbo.rpc.Filter
│ │ └── spring.factories
└── pom.xml
├── common-dubbo
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── itkevin
│ └── dubbo
│ └── common
│ └── filter
│ ├── LogApacheDubboFilter.java
│ └── LogApacheMDCClearDubboFilter.java
├── common-web
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── itkevin
│ │ └── web
│ │ └── common
│ │ ├── config
│ │ └── WebFilterConfig.java
│ │ ├── filter
│ │ └── LogWebFilter.java
│ │ ├── util
│ │ ├── CustomRequestWrapper.java
│ │ ├── HttpIPUtils.java
│ │ └── HttpRequestUtil.java
│ │ └── web
│ │ └── WebInitializer.java
│ └── resources
│ └── META-INF
│ └── spring.factories
├── common
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── itkevin
│ │ └── common
│ │ ├── config
│ │ ├── ApolloConfigTool.java
│ │ ├── ConfigTool.java
│ │ └── SysConfig.java
│ │ ├── constants
│ │ └── SysConstant.java
│ │ ├── enums
│ │ ├── AlarmToolEnum.java
│ │ ├── BusinessTypeEnum.java
│ │ ├── LogConstantEnum.java
│ │ ├── LogLevelEnum.java
│ │ ├── LogTypeEnum.java
│ │ ├── MDCConstantEnum.java
│ │ └── RequestTypeEnum.java
│ │ ├── listener
│ │ └── ConfigListener.java
│ │ ├── model
│ │ ├── BusinessData.java
│ │ ├── FilterMessage.java
│ │ ├── HashedWheelData.java
│ │ ├── LogCompressData.java
│ │ ├── LogData.java
│ │ ├── LogUriElapsedData.java
│ │ ├── TaskModel.java
│ │ ├── UriElapsedCollect.java
│ │ └── UriElapsedData.java
│ │ ├── notice
│ │ ├── MarkDownBaseMessage.java
│ │ ├── NoticeInterface.java
│ │ ├── NotifyMessageTools.java
│ │ ├── dingding
│ │ │ ├── DingConfigData.java
│ │ │ ├── DingMarkDownMessage.java
│ │ │ ├── DingTalkNotice.java
│ │ │ ├── DingTalkService.java
│ │ │ └── DingTextMessage.java
│ │ ├── model
│ │ │ └── BaseMessage.java
│ │ └── workwx
│ │ │ ├── WeWorkConfigData.java
│ │ │ ├── WeWorkMarkDownMessage.java
│ │ │ ├── WeWorkTextMessage.java
│ │ │ ├── WorkWeiXinTalkNotice.java
│ │ │ └── WorkWeiXinTalkService.java
│ │ └── util
│ │ ├── BatchConsumerCallbackAdvisor.java
│ │ ├── ByteBuddyPlugin.java
│ │ ├── ByteBuddyUtils.java
│ │ ├── CommonConverter.java
│ │ ├── ConfigUtils.java
│ │ ├── ConsumerCallbackAdvisor.java
│ │ ├── ElapsedUtils.java
│ │ ├── EventProcessorAdvisor.java
│ │ ├── GuavaCacheUtils.java
│ │ ├── HashedWheelTask.java
│ │ ├── HashedWheelUtils.java
│ │ ├── IPUtils.java
│ │ ├── JobHandlerAdvisor.java
│ │ ├── LocalCacheUtils.java
│ │ ├── LogUtils.java
│ │ ├── MD5Utils.java
│ │ ├── MDCUtils.java
│ │ ├── PropertiesUtils.java
│ │ ├── SimpleMetricsUtils.java
│ │ └── StringConverterFactory.java
│ └── resources
│ ├── META-INF
│ └── services
│ │ ├── com.itkevin.common.config.ConfigTool
│ │ └── com.itkevin.common.notice.NoticeInterface
│ ├── application.properties
│ └── logback.xml
├── img
├── img.png
├── 主要特性.png
└── 报警种类.png
├── pom.xml
└── userManual.md
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**
5 | !**/src/test/**
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | logs/
30 |
31 | ### VS Code ###
32 | .vscode/
--------------------------------------------------------------------------------
/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 |
2 |
📝sky-eye
3 |
4 |
5 |
6 |
7 |
8 | ## sky-eye:实时日志报警系统
9 |
10 | ### sky-eye是什么
11 | 目前市面上的日志预警系统绝大部分都是基于队列消费的方式进行异步处理,报警多数有延迟而且不够明确,
12 | 针对这个问题,我设计了一套基于日志filter的准实时报警系统,来帮助项目开发维护人员能够更加快速的
13 | 感知项目中的各种error日志,进而保障系统稳定性。
14 |
15 |
16 | ### sky-eye 架构设计
17 | sky-eye的主要特性:
18 | 
19 | 流程:
20 | 
21 |
22 | ### 快速接入
23 |
24 | #### 1、引入包
25 | ```xml
26 |
27 |
28 | com.itkevin
29 | log4j-api
30 | 1.0.0
31 |
32 |
33 |
34 |
35 | com.itkevin
36 | logback-api
37 | 1.0.0
38 |
39 | ```
40 |
41 | #### 2、设置设置启动参数
42 | ```java
43 | // log4j
44 | com.itkevin.log4j.api.listener.Log4jApplicationListener
45 | // logback
46 | com.itkevin.logback.api.listener.LogbackApplicationListener
47 | // 以上两个类交由spring管理,springboot项目通过自动配置的方式默认管理,不用其余配置
48 | ```
49 | #### 3、apollo配置参数
50 | 首先需要创建对应的namespace名称为:skyeye
51 | ```properties
52 | # 是否启动报警
53 | skyeye.log.alarm.enabled = true
54 | # 报警钉钉严重错误机器人配置(支持多个机器人)
55 | skyeye.log.alarm.serious.talk.hook = [ { "webHook": "https://oapi.dingtalk.com/robot/send?access_token=xxxxx", "secret": "xxxx" } ]
56 | # 报警钉钉机器人配置(支持多个机器人)
57 | skyeye.log.alarm.talk.hook = [ { "webHook": "https://oapi.dingtalk.com/robot/send?access_token=xxx", "secret": "xxxx" } ]
58 | # 堆栈行数配置
59 | skyeye.log.alarm.stackNum = 10
60 | # 单条报警白名单
61 | skyeye.log.alarm.white.list = 我是白名单
62 | # 聚合报警白名单
63 | skyeye.log.alarm.aggre.white.list = 我是聚合白名单
64 | # 报警间隔时间(单位分钟)
65 | skyeye.log.alarm.notify.time = 1
66 | # 报警次数阀值
67 | skyeye.log.alarm.notify.count = 1
68 | # 接口耗时报警间隔时间(单位分钟)
69 | skyeye.log.alarm.uri.elapsed.time = 1
70 | # 接口耗时超过阀值时间的次数阀值(阀值时间如果不指定则默认1000毫秒)
71 | skyeye.log.alarm.uri.elapsed.count = 10
72 | # 指定URI接口耗时时间阀值(单位毫秒,支持指定多个URI)
73 | skyeye.log.alarm.uri.elapsed = [{"uri":"/user/logTest","elapsed":2000}]
74 | # 指定接口耗时时间阀值(单位毫秒,全局指定,不配置默认1000毫秒)
75 | skyeye.log.alarm.uri.elapsed.global = 1000
76 | # 选择提醒工具
77 | skyeye.log.alarm.tool = wework
78 | # 设置需要监控的logAppender
79 | skyeye.log.appender = file
80 | ```
81 | #### 4、通知格式
82 | ```
83 | error信息:这是个错误的信息
84 | 服务名称:null
85 | 服务器IP:192.168.199.1
86 | 服务器hostname:localhost
87 | 发生时间:2021-08-22 16:56:06
88 | 请求类型:null
89 | 跟踪traceId:null
90 | 请求URI:null
91 | 异常信息:这是个exception
92 | 异常堆栈:
93 | 这是个堆栈信息
94 | ```
95 | #### 5、自定义通知接入方法
96 | - 1、实现`com.itkevin.common.notice.NoticeInterfacee`类,实现`com.itkevin.common.notice.NoticeInterface#sendMessage`和`com.itkevin.common.notice.NoticeInterface#filterFlag`两个方法
97 | - 2、在apollo上配置`skyeye.log.alarm.tool`属性,属性值设置为`com.itkevin.common.notice.NoticeInterface#filterFlag`方法的返回值
98 | - 3、在resources目录下创建META-INF.services文件夹创建名为`com.itkevin.common.notice.NoticeInterface`的文件,并将自己的实现类全路径写到这个文件中
99 | - 备注:目前`com.itkevin.common.notice.NoticeInterface#sendMessage`中暂时只支持markdown格式,后期会开放自定义格式
100 |
101 | #### 6、自定义配置中心接入方法
102 | - 1、实现`com.itkevin.common.config.ConfigTool`接口
103 | - 2、`com.itkevin.common.config.ConfigTool#getConfig`方法作为配置写入方法,需要把所有的配置信息返回到map中
104 | - 3、`com.itkevin.common.config.ConfigTool#sortFlag`方法是选取配置的方法,系统默认使用Apollo配置中心返回为0,想要使用自己的配置只需要返回一个大于0的数值就可以
105 | - 4、在resources目录下创建META-INF.services文件夹创建名为`com.itkevin.common.config.ConfigTool`的文件,并将自己的实现类全路径写到这个文件中
106 | - 备注:选取配置中心的方法可能不是很优雅,后面版本可能会修改
107 |
108 | #### 7、建议配置
109 | 建议在maven配置中添加强制版本号插件
110 | ```xml
111 |
112 | org.apache.maven.plugins
113 | maven-enforcer-plugin
114 | 3.0.0-M2
115 |
116 |
117 | enforce-versions
118 |
119 | enforce
120 |
121 |
122 |
123 |
124 | true
125 | 请排除jar包冲突:okhttp:3.14.4以下版本,hutool-all:5.3.2以下版本,slf4j-log4j12:1.7.5以下版本
126 |
127 | com.squareup.okhttp3:okhttp:(,3.14.4)
128 | cn.hutool:hutool-all:(,5.3.2)
129 | org.slf4j:slf4j-log4j12:(,1.7.5)
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 | ```
138 |
139 | #### 备注
140 | - 目前接口超时报警仅支持web,后期会同步支持部分的rpc框架
141 | - 目前仅支持apollo配置和钉钉报警,后期会开放配置接口,可自行选择配置,报警接口也会支持企业微信和飞书,并且开放通知接口,可自定接入报警
142 |
143 |
144 |
--------------------------------------------------------------------------------
/api/log4j-api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | sky-eye-api
7 | com.itkevin
8 | 2.0
9 |
10 | 4.0.0
11 |
12 | log4j-api
13 | 2.0.0
14 | jar
15 |
16 |
17 | 8
18 | 8
19 | 2.0.0
20 | 1.2.17
21 |
22 |
23 |
24 |
25 |
26 | com.itkevin
27 | sky-eye-web-common
28 | ${skyeye-common.version}
29 |
30 |
31 | com.itkevin
32 | sky-eye-common
33 | ${skyeye-common.version}
34 |
35 |
36 | log4j
37 | log4j
38 | ${log4j.version}
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/api/log4j-api/src/main/java/com/itkevin/log4j/api/config/Log4jConfig.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.log4j.api.config;
2 |
3 | import com.itkevin.log4j.api.listener.Log4jApplicationListener;
4 | import com.itkevin.web.common.filter.LogWebFilter;
5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
7 | import org.springframework.boot.web.servlet.FilterRegistrationBean;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.context.annotation.Configuration;
10 |
11 | import javax.servlet.Filter;
12 |
13 | /**
14 | * log4j config
15 | */
16 | @Configuration
17 | @ConditionalOnClass(
18 | value = {org.apache.log4j.Logger.class, org.apache.log4j.spi.Filter .class}
19 | )
20 | public class Log4jConfig {
21 |
22 | @Bean
23 | @ConditionalOnMissingBean
24 | public Log4jApplicationListener logbackApplicationListener() {
25 | return new Log4jApplicationListener();
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/api/log4j-api/src/main/java/com/itkevin/log4j/api/filter/Log4jFilter.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.log4j.api.filter;
2 |
3 | import cn.hutool.core.date.DateUtil;
4 | import com.itkevin.common.config.SysConfig;
5 | import com.itkevin.common.constants.SysConstant;
6 | import com.itkevin.common.enums.LogTypeEnum;
7 | import com.itkevin.common.model.FilterMessage;
8 | import com.itkevin.common.model.LogData;
9 | import com.itkevin.common.util.LocalCacheUtils;
10 | import com.itkevin.common.util.LogUtils;
11 | import com.itkevin.common.notice.NotifyMessageTools;
12 | import lombok.extern.slf4j.Slf4j;
13 | import org.apache.commons.lang3.exception.ExceptionUtils;
14 | import org.apache.log4j.Level;
15 | import org.apache.log4j.spi.Filter;
16 | import org.apache.log4j.spi.LoggingEvent;
17 | import org.apache.log4j.spi.ThrowableInformation;
18 |
19 | import java.util.Date;
20 | import java.util.Optional;
21 |
22 | /**
23 | * log4j Filter
24 | */
25 | @Slf4j
26 | public class Log4jFilter extends Filter {
27 |
28 | @Override
29 | public int decide(LoggingEvent event) {
30 | try {
31 | // error日志
32 | if (Level.ERROR.equals(event.getLevel())) {
33 | if (SysConfig.instance.getAlarmEnabled()) {
34 | // error信息
35 | String msg = event.getRenderedMessage() != null ? event.getRenderedMessage().trim() : "";
36 | // 获取异常
37 | Throwable ex = Optional.of(event).map(LoggingEvent::getThrowableInformation).map(ThrowableInformation::getThrowable).orElse(null);
38 | // 异常message、异常堆栈
39 | String message = ex != null ? ex.getMessage() : "";
40 | String stackTrace = ex != null ? ExceptionUtils.getStackTrace(ex) : "";
41 | // 过滤信息
42 | FilterMessage filterMessage = new FilterMessage();
43 | filterMessage.setLogType(LogTypeEnum.LOG4J.name());
44 | filterMessage.setMsg(msg);
45 | filterMessage.setMessage(message);
46 | filterMessage.setStackTrace(stackTrace);
47 | // 获取logData对象
48 | LogData logData = LogUtils.obtainLogData(LogTypeEnum.LOG4J.name(), msg, message, stackTrace);
49 | logData.setOccurrenceTime(DateUtil.formatDateTime(new Date(event.getTimeStamp())));
50 | logData.setFilter(LogUtils.filter(filterMessage));
51 | // 发送消息
52 | NotifyMessageTools.getInstance().sendMessage(logData);
53 | // 异常报警上报
54 | if (!logData.getFilter() && !SysConstant.WELCOME.equals(logData.getErrorMessage())) {
55 | LocalCacheUtils.incr(SysConstant.ALARM_METRIC_NAME);
56 | }
57 | }
58 | }
59 | } catch (Throwable e) {
60 | log.warn("log skyeye >>> Log4jFilter occur exception", e);
61 | }
62 |
63 | return Filter.NEUTRAL;
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/api/log4j-api/src/main/java/com/itkevin/log4j/api/listener/Log4jApplicationListener.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.log4j.api.listener;
2 |
3 | import com.ctrip.framework.apollo.Config;
4 | import com.google.common.collect.Lists;
5 | import com.itkevin.common.config.ConfigTool;
6 | import com.itkevin.common.config.SysConfig;
7 | import com.itkevin.common.constants.SysConstant;
8 | import com.itkevin.common.listener.ConfigListener;
9 | import com.itkevin.common.util.HashedWheelTask;
10 | import com.itkevin.log4j.api.filter.Log4jFilter;
11 | import lombok.extern.slf4j.Slf4j;
12 | import org.apache.log4j.Appender;
13 | import org.apache.log4j.Logger;
14 | import org.apache.log4j.spi.Filter;
15 | import org.springframework.context.ApplicationContext;
16 | import org.springframework.context.ApplicationListener;
17 | import org.springframework.context.event.ContextRefreshedEvent;
18 | import org.springframework.util.CollectionUtils;
19 |
20 | import java.util.Comparator;
21 | import java.util.List;
22 | import java.util.Map;
23 | import java.util.ServiceLoader;
24 | import java.util.Set;
25 |
26 | /**
27 | * log4j初始化
28 | */
29 | @Slf4j
30 | public class Log4jApplicationListener implements ApplicationListener {
31 |
32 | @Override
33 | public void onApplicationEvent(ContextRefreshedEvent event) {
34 | // 配置放入缓存
35 | ServiceLoader serviceLoader = ServiceLoader.load(ConfigTool.class);
36 | List configTools = Lists.newArrayList();
37 | for (ConfigTool configTool : serviceLoader) {
38 | configTools.add(configTool);
39 | }
40 | configTools.stream().max(Comparator.comparing(ConfigTool::sortFlag)).ifPresent(configTool -> {
41 | Map map = configTool.getConfig();
42 | SysConfig.convertMap2SysConfig(map);
43 | });
44 |
45 | // 设置log filter,初始化动作
46 | Logger logger = Logger.getRootLogger();
47 | Appender fileAppender = logger.getAppender("file");
48 | if (fileAppender != null) {
49 | Filter filter = fileAppender.getFilter();
50 | if (!(filter instanceof Log4jFilter)) {
51 | fileAppender.addFilter(new Log4jFilter());
52 | // 初始化任务调度器
53 | HashedWheelTask.init();
54 | // 项目启动后发送欢迎语
55 | log.error(SysConstant.WELCOME);
56 | }
57 | }
58 | }
59 |
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/api/log4j-api/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter:
--------------------------------------------------------------------------------
1 | logApacheMDCClearDubboFilter=com.skyeye.k12.teacher.common.filter.LogApacheMDCClearDubboFilter
2 | logApacheDubboFilter=com.skyeye.k12.teacher.common.filter.LogApacheDubboFilter
--------------------------------------------------------------------------------
/api/log4j-api/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2 | com.itkevin.log4j.api.config.Log4jConfig
--------------------------------------------------------------------------------
/api/logback-api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | sky-eye-api
7 | com.itkevin
8 | 2.0
9 |
10 | 4.0.0
11 |
12 | logback-api
13 | 2.0.0
14 | jar
15 |
16 |
17 | 8
18 | 8
19 | 2.0.0
20 | 1.2.3
21 |
22 |
23 |
24 |
25 | com.itkevin
26 | sky-eye-web-common
27 | ${skyeye-common.version}
28 |
29 |
30 | com.itkevin
31 | sky-eye-common
32 | ${skyeye-common.version}
33 |
34 |
35 | slf4j-api
36 | org.slf4j
37 |
38 |
39 |
40 |
41 | ch.qos.logback
42 | logback-classic
43 | ${logback-classic.version}
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/api/logback-api/src/main/java/com/itkevin/logback/api/config/LogbackConfig.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.logback.api.config;
2 |
3 | import com.itkevin.logback.api.listener.LogbackApplicationListener;
4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | /**
10 | * log config
11 | */
12 | @Configuration
13 | @ConditionalOnClass(
14 | value = {ch.qos.logback.classic.Logger.class, ch.qos.logback.core.filter.Filter.class}
15 | )
16 | public class LogbackConfig {
17 |
18 | @Bean
19 | @ConditionalOnMissingBean
20 | public LogbackApplicationListener logbackApplicationListener() {
21 | return new LogbackApplicationListener();
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/api/logback-api/src/main/java/com/itkevin/logback/api/filter/LogbackFilter.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.logback.api.filter;
2 |
3 | import ch.qos.logback.classic.Level;
4 | import ch.qos.logback.classic.spi.ILoggingEvent;
5 | import ch.qos.logback.classic.spi.IThrowableProxy;
6 | import ch.qos.logback.classic.spi.ThrowableProxy;
7 | import ch.qos.logback.core.filter.Filter;
8 | import ch.qos.logback.core.spi.FilterReply;
9 | import cn.hutool.core.date.DateUtil;
10 | import com.itkevin.common.config.SysConfig;
11 | import com.itkevin.common.constants.SysConstant;
12 | import com.itkevin.common.enums.LogTypeEnum;
13 | import com.itkevin.common.model.FilterMessage;
14 | import com.itkevin.common.model.LogData;
15 | import com.itkevin.common.util.LocalCacheUtils;
16 | import com.itkevin.common.util.LogUtils;
17 | import com.itkevin.common.notice.NotifyMessageTools;
18 | import lombok.extern.slf4j.Slf4j;
19 | import org.apache.commons.lang3.exception.ExceptionUtils;
20 |
21 | import java.util.Date;
22 |
23 | /**
24 | * logback filter
25 | */
26 | @Slf4j
27 | public class LogbackFilter extends Filter {
28 |
29 | @Override
30 | public FilterReply decide(ILoggingEvent event) {
31 | try {
32 | // error日志
33 | if (Level.ERROR.equals(event.getLevel())) {
34 | if (SysConfig.instance.getAlarmEnabled()) {
35 | // error信息
36 | String msg = event.getFormattedMessage() != null ? event.getFormattedMessage().trim() : "";
37 | // 异常message、异常堆栈
38 | String message = "";
39 | String stackTrace = "";
40 | // 获取异常
41 | IThrowableProxy iThrowableProxy = event.getThrowableProxy();
42 | if (iThrowableProxy instanceof ThrowableProxy) {
43 | ThrowableProxy throwableProxy = (ThrowableProxy) iThrowableProxy;
44 | Throwable ex = throwableProxy.getThrowable();
45 | message = ex.getMessage();
46 | stackTrace = ExceptionUtils.getStackTrace(ex);
47 | }
48 | // 过滤信息
49 | FilterMessage filterMessage = new FilterMessage();
50 | filterMessage.setLogType(LogTypeEnum.LOGBACK.name());
51 | filterMessage.setMsg(msg);
52 | filterMessage.setMessage(message);
53 | filterMessage.setStackTrace(stackTrace);
54 | // 获取logData对象
55 | LogData logData = LogUtils.obtainLogData(LogTypeEnum.LOGBACK.name(), msg, message, stackTrace);
56 | logData.setOccurrenceTime(DateUtil.formatDateTime(new Date(event.getTimeStamp())));
57 | logData.setFilter(LogUtils.filter(filterMessage));
58 | // 发送消息
59 | NotifyMessageTools.getInstance().sendMessage(logData);
60 | // 异常报警上报
61 | if (!logData.getFilter() && !SysConstant.WELCOME.equals(logData.getErrorMessage())) {
62 | LocalCacheUtils.incr(SysConstant.ALARM_METRIC_NAME);
63 | }
64 | }
65 | }
66 | } catch (Throwable e) {
67 | log.warn("log skyeye >>> LogbackFilter occur exception", e);
68 | }
69 |
70 | return FilterReply.NEUTRAL;
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/api/logback-api/src/main/java/com/itkevin/logback/api/listener/LogbackApplicationListener.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.logback.api.listener;
2 |
3 | import ch.qos.logback.classic.Logger;
4 | import ch.qos.logback.classic.LoggerContext;
5 | import ch.qos.logback.classic.spi.ILoggingEvent;
6 | import ch.qos.logback.core.Appender;
7 | import ch.qos.logback.core.filter.Filter;
8 | import com.google.common.collect.Lists;
9 | import com.itkevin.common.config.ConfigTool;
10 | import com.itkevin.common.config.SysConfig;
11 | import com.itkevin.common.constants.SysConstant;
12 | import com.itkevin.common.util.HashedWheelTask;
13 | import com.itkevin.logback.api.filter.LogbackFilter;
14 | import lombok.extern.slf4j.Slf4j;
15 | import org.slf4j.LoggerFactory;
16 | import org.springframework.context.ApplicationListener;
17 | import org.springframework.context.event.ContextRefreshedEvent;
18 |
19 | import java.util.Comparator;
20 | import java.util.List;
21 | import java.util.Map;
22 | import java.util.ServiceLoader;
23 |
24 | /**
25 | * logback初始化
26 | */
27 | @Slf4j
28 | public class LogbackApplicationListener implements ApplicationListener {
29 |
30 | @Override
31 | public void onApplicationEvent(ContextRefreshedEvent event) {
32 | // 配置放入缓存
33 | ServiceLoader serviceLoader = ServiceLoader.load(ConfigTool.class);
34 | List configTools = Lists.newArrayList();
35 | for (ConfigTool configTool : serviceLoader) {
36 | configTools.add(configTool);
37 | }
38 | configTools.stream().max(Comparator.comparing(ConfigTool::sortFlag)).ifPresent(configTool -> {
39 | Map map = configTool.getConfig();
40 | SysConfig.convertMap2SysConfig(map);
41 | });
42 |
43 | // 设置log filter,初始化动作
44 | LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
45 | Logger logger = context.getLogger(Logger.ROOT_LOGGER_NAME);
46 | Appender fileAppenderERROR = logger.getAppender("console");
47 | Appender fileAppender = fileAppenderERROR == null ? logger.getAppender(SysConfig.instance.getSkyeyeLogAppender()) : fileAppenderERROR;
48 | if (fileAppender != null) {
49 | List> filters = fileAppender.getCopyOfAttachedFiltersList();
50 | for (Filter filter : filters) {
51 | if (filter instanceof LogbackFilter) {
52 | return;
53 | }
54 | }
55 | fileAppender.addFilter(new LogbackFilter());
56 | // 初始化任务调度器
57 | HashedWheelTask.init();
58 | // 项目启动后发送欢迎语
59 | log.error(SysConstant.WELCOME);
60 | }
61 | }
62 |
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/api/logback-api/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter:
--------------------------------------------------------------------------------
1 | logApacheMDCClearDubboFilter=com.skyeye.k12.teacher.common.filter.LogApacheMDCClearDubboFilter
2 | logApacheDubboFilter=com.skyeye.k12.teacher.common.filter.LogApacheDubboFilter
--------------------------------------------------------------------------------
/api/logback-api/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2 | com.itkevin.logback.api.config.LogbackConfig
--------------------------------------------------------------------------------
/api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | sky-eye
7 | com.itkevin
8 | 2.0
9 |
10 | 4.0.0
11 |
12 | sky-eye-api
13 | pom
14 | 2.0
15 |
16 |
17 | log4j-api
18 | logback-api
19 |
20 |
21 |
22 | 8
23 | 8
24 |
25 |
26 |
--------------------------------------------------------------------------------
/common-dubbo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | sky-eye
7 | com.itkevin
8 | 2.0
9 |
10 | 4.0.0
11 | 2.0.0
12 | jar
13 |
14 | sky-eye-dubbo-common
15 |
16 |
17 | 8
18 | 8
19 | 2.0.0
20 |
21 |
22 |
23 |
24 |
25 | com.itkevin
26 | sky-eye-common
27 | ${skyeye-common.version}
28 |
29 |
30 |
31 | org.apache.dubbo
32 | dubbo
33 | 2.7.7
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/common-dubbo/src/main/java/com/itkevin/dubbo/common/filter/LogApacheDubboFilter.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.dubbo.common.filter;
2 |
3 | import com.itkevin.common.enums.MDCConstantEnum;
4 | import com.itkevin.common.enums.RequestTypeEnum;
5 | import com.itkevin.common.model.UriElapsedCollect;
6 | import com.itkevin.common.util.ElapsedUtils;
7 | import com.itkevin.common.util.MDCUtils;
8 | import lombok.extern.slf4j.Slf4j;
9 | import org.apache.dubbo.common.constants.CommonConstants;
10 | import org.apache.dubbo.common.extension.Activate;
11 | import org.apache.dubbo.rpc.Filter;
12 | import org.apache.dubbo.rpc.Invocation;
13 | import org.apache.dubbo.rpc.Invoker;
14 | import org.apache.dubbo.rpc.Result;
15 | import org.apache.dubbo.rpc.RpcContext;
16 | import org.apache.dubbo.rpc.RpcException;
17 | import org.apache.skywalking.apm.toolkit.trace.TraceContext;
18 |
19 | import java.util.Arrays;
20 |
21 | @Slf4j
22 | @Activate(
23 | group = {CommonConstants.PROVIDER_SIDE},
24 | order = 1
25 | )
26 | public class LogApacheDubboFilter implements Filter {
27 |
28 | @Override
29 | public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {
30 | long start = 0;
31 | try {
32 | mdc(invoker, invocation);
33 | start = System.currentTimeMillis();
34 | return invoker.invoke(invocation);
35 | } finally {
36 | UriElapsedCollect uriElapsedCollect = ElapsedUtils.uriElapsedCollect(System.currentTimeMillis() - start);
37 | ElapsedUtils.uriElapsed(uriElapsedCollect);
38 | }
39 | }
40 |
41 | /**
42 | * mdc上下文
43 | * @param invoker
44 | * @param invocation
45 | */
46 | private void mdc(Invoker> invoker, Invocation invocation) {
47 | try {
48 | RpcContext rpcContext = RpcContext.getContext();
49 | MDCUtils.put(MDCConstantEnum.SERVER_NAME.getCode(), rpcContext.getUrl().getParameter("application"));
50 | MDCUtils.put(MDCConstantEnum.SOURCE_IP.getCode(), rpcContext.getRemoteHost());
51 | MDCUtils.put(MDCConstantEnum.SERVER_IP.getCode(), rpcContext.getLocalHost());
52 | // MDCUtils.put(MDCConstantEnum.SERVER_HOSTNAME.getCode(), IPUtils.getLocalHostName());
53 | MDCUtils.put(MDCConstantEnum.REQUEST_TYPE.getCode(), RequestTypeEnum.DUBBO.name().toLowerCase());
54 | MDCUtils.put(MDCConstantEnum.TRACE_ID.getCode(), TraceContext.traceId());
55 | MDCUtils.put(MDCConstantEnum.REQUEST_URI.getCode(), invoker.getInterface().getName() + "#" + invocation.getMethodName());
56 | MDCUtils.put(MDCConstantEnum.REQUEST_PARAM.getCode(), Arrays.asList(invocation.getArguments()).toString());
57 | } catch (Exception e) {
58 | log.warn("log skyeye >>> LogApacheDubboFilter occur exception", e);
59 | }
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/common-dubbo/src/main/java/com/itkevin/dubbo/common/filter/LogApacheMDCClearDubboFilter.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.dubbo.common.filter;
2 |
3 | import com.itkevin.common.util.MDCUtils;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.apache.dubbo.common.constants.CommonConstants;
6 | import org.apache.dubbo.common.extension.Activate;
7 | import org.apache.dubbo.rpc.*;
8 |
9 | @Slf4j
10 | @Activate(
11 | group = {CommonConstants.PROVIDER_SIDE},
12 | order = -1
13 | )
14 | public class LogApacheMDCClearDubboFilter implements Filter {
15 |
16 | @Override
17 | public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {
18 | try {
19 | return invoker.invoke(invocation);
20 | } finally {
21 | MDCUtils.clear();
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/common-web/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | sky-eye
7 | com.itkevin
8 | 2.0
9 |
10 | 4.0.0
11 | 2.0.0
12 | jar
13 |
14 | sky-eye-web-common
15 |
16 |
17 | 8
18 | 8
19 | 2.0.0
20 |
21 |
22 |
23 |
24 | com.itkevin
25 | sky-eye-common
26 | ${skyeye-common.version}
27 |
28 |
29 | okhttp
30 | com.squareup.okhttp3
31 |
32 |
33 | gson
34 | com.google.code.gson
35 |
36 |
37 | retrofit
38 | com.squareup.retrofit2
39 |
40 |
41 | slf4j-api
42 | org.slf4j
43 |
44 |
45 | reactor-core
46 | io.projectreactor
47 |
48 |
49 |
50 |
51 |
52 | org.springframework.boot
53 | spring-boot-starter-web
54 | 2.1.6.RELEASE
55 |
56 |
57 |
58 | javax.servlet
59 | servlet-api
60 | 2.5
61 | provided
62 |
63 |
64 |
--------------------------------------------------------------------------------
/common-web/src/main/java/com/itkevin/web/common/config/WebFilterConfig.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.web.common.config;
2 |
3 | import com.itkevin.web.common.filter.LogWebFilter;
4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
5 | import org.springframework.boot.web.servlet.FilterRegistrationBean;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | import javax.servlet.Filter;
10 |
11 | @Configuration
12 | @ConditionalOnClass(
13 | value = {ch.qos.logback.classic.Logger.class, ch.qos.logback.core.filter.Filter.class}
14 | )
15 | public class WebFilterConfig {
16 |
17 | @Bean
18 | public Filter logWebFilter() {
19 | return new LogWebFilter();
20 | }
21 |
22 | @Bean
23 | public FilterRegistrationBean logWebFilterBean() {
24 | FilterRegistrationBean registration = new FilterRegistrationBean();
25 | registration.setFilter(logWebFilter());
26 | registration.addUrlPatterns("/*");
27 | return registration;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/common-web/src/main/java/com/itkevin/web/common/filter/LogWebFilter.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.web.common.filter;
2 |
3 | import com.itkevin.common.model.UriElapsedCollect;
4 | import com.itkevin.web.common.util.CustomRequestWrapper;
5 | import com.itkevin.common.util.ElapsedUtils;
6 | import com.itkevin.web.common.util.HttpRequestUtil;
7 | import com.itkevin.common.util.MDCUtils;
8 | import org.apache.commons.lang3.StringUtils;
9 | import org.springframework.http.MediaType;
10 |
11 | import javax.servlet.Filter;
12 | import javax.servlet.FilterChain;
13 | import javax.servlet.FilterConfig;
14 | import javax.servlet.ServletException;
15 | import javax.servlet.ServletRequest;
16 | import javax.servlet.ServletResponse;
17 | import javax.servlet.http.HttpServletRequest;
18 | import java.io.IOException;
19 |
20 | /**
21 | * log servlet filter
22 | */
23 | public class LogWebFilter implements Filter {
24 |
25 | @Override
26 | public void init(FilterConfig filterConfig) throws ServletException {
27 |
28 | }
29 |
30 | @Override
31 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
32 | long start = 0;
33 | try {
34 | HttpServletRequest request = (HttpServletRequest) servletRequest;
35 | String contentType = request.getContentType();
36 | if (StringUtils.isNotBlank(contentType) && contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
37 | HttpRequestUtil.mdc(request);
38 | start = System.currentTimeMillis();
39 | filterChain.doFilter(servletRequest, servletResponse);
40 | } else {
41 | CustomRequestWrapper requestWrapper = new CustomRequestWrapper(request);
42 | HttpRequestUtil.mdc(requestWrapper);
43 | start = System.currentTimeMillis();
44 | filterChain.doFilter(requestWrapper, servletResponse);
45 | }
46 | } finally {
47 | UriElapsedCollect uriElapsedCollect = ElapsedUtils.uriElapsedCollect(System.currentTimeMillis() - start);
48 | ElapsedUtils.uriElapsed(uriElapsedCollect);
49 | MDCUtils.clear();
50 | }
51 | }
52 |
53 | @Override
54 | public void destroy() {
55 |
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/common-web/src/main/java/com/itkevin/web/common/util/CustomRequestWrapper.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.web.common.util;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 |
5 | import javax.servlet.ReadListener;
6 | import javax.servlet.ServletInputStream;
7 | import javax.servlet.http.HttpServletRequest;
8 | import javax.servlet.http.HttpServletRequestWrapper;
9 | import java.io.BufferedReader;
10 | import java.io.ByteArrayInputStream;
11 | import java.io.IOException;
12 | import java.io.InputStreamReader;
13 | import java.nio.charset.Charset;
14 | import java.util.Enumeration;
15 |
16 | public class CustomRequestWrapper extends HttpServletRequestWrapper {
17 | private final byte[] paramArray;
18 |
19 | private final String paramStr;
20 |
21 | /**
22 | * Constructs a request object wrapping the given request.
23 | *
24 | * @param request
25 | * @throws IllegalArgumentException if the request is null
26 | */
27 | public CustomRequestWrapper(HttpServletRequest request) {
28 | super(request);
29 | paramStr = HttpRequestUtil.getParamString(request);
30 | paramArray = paramStr.getBytes(Charset.forName("UTF-8"));
31 | }
32 |
33 | public String getParamStr() {
34 | return paramStr;
35 | }
36 |
37 |
38 | @Override
39 | public BufferedReader getReader() throws IOException {
40 | if (StringUtils.isBlank(paramStr)) {
41 | return super.getReader();
42 | }
43 | return new BufferedReader(new InputStreamReader(getInputStream()));
44 | }
45 |
46 | @Override
47 | public ServletInputStream getInputStream() throws IOException {
48 | if (StringUtils.isBlank(paramStr)) {
49 | return super.getInputStream();
50 | }
51 | final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(paramArray);
52 | return new ServletInputStream() {
53 | @Override
54 | public boolean isFinished() {
55 | return false;
56 | }
57 |
58 | @Override
59 | public boolean isReady() {
60 | return true;
61 | }
62 |
63 | @Override
64 | public void setReadListener(ReadListener readListener) {
65 |
66 | }
67 |
68 | @Override
69 | public int read() throws IOException {
70 | return byteArrayInputStream.read();
71 | }
72 | };
73 | }
74 |
75 | @Override
76 | public String getHeader(String name) {
77 | return super.getHeader(name);
78 | }
79 |
80 | @Override
81 | public Enumeration getHeaderNames() {
82 | return super.getHeaderNames();
83 | }
84 |
85 | @Override
86 | public Enumeration getHeaders(String name) {
87 | return super.getHeaders(name);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/common-web/src/main/java/com/itkevin/web/common/util/HttpIPUtils.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.web.common.util;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 |
5 | import javax.servlet.http.HttpServletRequest;
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.net.InetAddress;
9 | import java.net.NetworkInterface;
10 | import java.net.UnknownHostException;
11 | import java.util.Enumeration;
12 |
13 | public class HttpIPUtils {
14 | private static String hostName;
15 |
16 | /**
17 | * 获取请求IP
18 | * @param request
19 | * @return
20 | */
21 | public static String getIPAddress(HttpServletRequest request) {
22 | if (request == null) {
23 | return "unknown";
24 | }
25 | String ip = request.getHeader("x-forwarded-for");
26 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
27 | ip = request.getHeader("Proxy-Client-IP");
28 | }
29 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
30 | ip = request.getHeader("X-Forwarded-For");
31 | }
32 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
33 | ip = request.getHeader("WL-Proxy-Client-IP");
34 | }
35 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
36 | ip = request.getHeader("X-Real-IP");
37 | }
38 |
39 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
40 | ip = request.getRemoteAddr();
41 | if (ip.equals("127.0.0.1") || ip.equals("0:0:0:0:0:0:0:1")) {
42 | // 根据网卡取本机配置的IP
43 | InetAddress inet = null;
44 | try {
45 | inet = InetAddress.getLocalHost();
46 | } catch (UnknownHostException e) {
47 | e.printStackTrace();
48 | }
49 | ip = inet.getHostAddress();
50 | }
51 | }
52 | // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
53 | if (ip != null && ip.length() > 15) {
54 | if (ip.indexOf(",") > 0) {
55 | ip = ip.substring(0, ip.indexOf(","));
56 | }
57 | }
58 |
59 | return ip;
60 | }
61 |
62 | /**
63 | * 获取本机ip
64 | * @return
65 | */
66 | public static String getLocalIp() {
67 | try {
68 | //一个主机有多个网络接口
69 | Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces();
70 | while (netInterfaces.hasMoreElements()) {
71 | NetworkInterface netInterface = netInterfaces.nextElement();
72 | //每个网络接口,都会有多个"网络地址",比如一定会有loopback地址,会有siteLocal地址等.以及IPV4或者IPV6 .
73 | Enumeration addresses = netInterface.getInetAddresses();
74 | while (addresses.hasMoreElements()) {
75 | InetAddress address = addresses.nextElement();
76 | //get only :172.*,192.*,10.*
77 | if (address.isSiteLocalAddress() && !address.isLoopbackAddress()) {
78 | return address.getHostAddress();
79 | }
80 | }
81 | }
82 | }catch (Exception e) {
83 | e.printStackTrace();
84 | return null;
85 | }
86 | return null;
87 | }
88 |
89 | /**
90 | * 获取本机hostname
91 | * @return
92 | */
93 | public static String getLocalHostName() {
94 | if (StringUtils.isNotBlank(hostName)) {
95 | return hostName;
96 | }
97 | String hostname = null;
98 | try {
99 | hostname = InetAddress.getLocalHost().getHostName();
100 | } catch (UnknownHostException uhe) {
101 | String host = uhe.getMessage();
102 | if (host != null) {
103 | int colon = host.indexOf(':');
104 | if (colon > 0) {
105 | return host.substring(0, colon);
106 | }
107 | }
108 | }
109 | if (StringUtils.isNotBlank(hostname) && hostname.contains("-")) {
110 | hostName = hostname;
111 | return hostname;
112 | }
113 | InputStream is = null;
114 | try {
115 | Process p = Runtime.getRuntime().exec("hostname");
116 | byte[] hostBytes = new byte[1024];
117 | is = p.getInputStream();
118 | int readed = is.read(hostBytes);
119 | p.waitFor();
120 | hostName = new String(hostBytes, 0, readed);
121 | if (StringUtils.isNotBlank(hostName)) {
122 | hostName = hostName.trim();
123 | }
124 | return hostName;
125 | } catch (Throwable e) {
126 | e.printStackTrace();
127 | } finally {
128 | if (is != null) {
129 | try {
130 | is.close();
131 | } catch (IOException e) {
132 | e.printStackTrace();
133 | }
134 | }
135 | }
136 | hostName = "unknownHostname";
137 | return hostName;
138 | }
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/common-web/src/main/java/com/itkevin/web/common/util/HttpRequestUtil.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.web.common.util;
2 |
3 | import com.itkevin.common.enums.MDCConstantEnum;
4 | import com.itkevin.common.util.MDCUtils;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.apache.skywalking.apm.toolkit.trace.TraceContext;
7 | import org.springframework.http.MediaType;
8 |
9 | import javax.servlet.ServletInputStream;
10 | import javax.servlet.http.HttpServletRequest;
11 | import java.io.BufferedReader;
12 | import java.io.IOException;
13 | import java.io.InputStreamReader;
14 | import java.nio.charset.Charset;
15 | import java.util.Map;
16 |
17 | @Slf4j
18 | public class HttpRequestUtil {
19 |
20 | /**
21 | * 获取请求参数
22 | * try-with-resource 执行完 try 块后会释放声明的资源(实现了 AutoCloseable 接口的类对象)
23 | * @param servletRequest
24 | * @return
25 | */
26 | public static String getParamString(HttpServletRequest servletRequest) {
27 | String result = "";
28 | String contentType = servletRequest.getContentType();
29 |
30 | if(contentType==null) {
31 | Map parameterMap = servletRequest.getParameterMap();
32 | StringBuilder stringBuilder = new StringBuilder();
33 | for(String key : parameterMap.keySet()) {
34 | stringBuilder.append(convertStr(key,parameterMap.get(key)));
35 | }
36 | result = stringBuilder.toString();
37 | } else {
38 | if (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(contentType)) {
39 | Map parameterMap = servletRequest.getParameterMap();
40 | StringBuilder stringBuilder = new StringBuilder();
41 | for(String key : parameterMap.keySet()) {
42 | stringBuilder.append(convertStr(key,parameterMap.get(key)));
43 | }
44 | result = stringBuilder.toString();
45 | } else if (MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(contentType)
46 | || MediaType.APPLICATION_JSON_UTF8_VALUE.equalsIgnoreCase(contentType)
47 | || MediaType.TEXT_PLAIN_VALUE.equalsIgnoreCase(contentType)){
48 | try (ServletInputStream inputStream = servletRequest.getInputStream();
49 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))) )
50 | {
51 | StringBuilder sb = new StringBuilder();
52 | String line;
53 | while ((line=reader.readLine())!=null) {
54 | sb.append(line);
55 | }
56 | result = sb.toString();
57 | } catch (IOException e) {
58 | log.warn("log skyeye >>> Failed to get the binary data stream contained in the request body! Url is '{}'.", servletRequest.getRequestURL().toString(), e);
59 | }
60 | }
61 | }
62 |
63 | return result;
64 | }
65 |
66 |
67 | /**
68 | * 使用 , 连接字符串,末尾是空格
69 | * @param values
70 | * @param key
71 | * @return key=value1,value2,value3
72 | */
73 | public static String convertStr(String key,String[] values) {
74 | if(values==null){
75 | return null;
76 | }
77 | int length = values.length;
78 | StringBuilder stringBuilder = new StringBuilder(length);
79 | stringBuilder.append(key).append("=");
80 | for(int i=0;i>> LogWebFilter occur exception", e);
107 | }
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/common-web/src/main/java/com/itkevin/web/common/web/WebInitializer.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.web.common.web;
2 |
3 | import com.itkevin.web.common.filter.LogWebFilter;
4 | import org.springframework.web.WebApplicationInitializer;
5 |
6 | import javax.servlet.DispatcherType;
7 | import javax.servlet.FilterRegistration;
8 | import javax.servlet.ServletContext;
9 | import javax.servlet.ServletException;
10 | import java.util.EnumSet;
11 |
12 | /**
13 | * servlet初始化类
14 | */
15 | public class WebInitializer implements WebApplicationInitializer {
16 |
17 | @Override
18 | public void onStartup(ServletContext servletContext) throws ServletException {
19 | FilterRegistration filterRegistration = servletContext.getFilterRegistration("LogWebFilter");
20 | if (filterRegistration == null) {
21 | FilterRegistration.Dynamic logWebFilter = servletContext.addFilter("LogWebFilter", LogWebFilter.class);
22 | logWebFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/common-web/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2 | com.itkevin.web.common.config.WebFilterConfig
--------------------------------------------------------------------------------
/common/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | sky-eye
7 | com.itkevin
8 | 2.0
9 |
10 | 4.0.0
11 |
12 | sky-eye-common
13 | 2.0.0
14 | jar
15 |
16 |
17 | 8
18 | 8
19 | 1.5.4
20 | 6.6.0
21 | 5.3.2
22 | 3.8.1
23 | 1.4.6
24 | 2.7.1
25 | 3.2.11.RELEASE
26 | 2.1.0
27 | 1.0.0
28 | 2.0.2
29 | 1.3.3.RELEASE
30 | 2.1.0.14
31 |
32 |
33 |
34 |
35 |
36 | cn.hutool
37 | hutool-all
38 | ${hutool-all.version}
39 |
40 |
41 |
42 | org.apache.commons
43 | commons-lang3
44 | ${commons-lang3.version}
45 |
46 |
47 |
48 | io.projectreactor
49 | reactor-core
50 | ${reactor-core.version}
51 |
52 |
53 |
54 | net.bytebuddy
55 | byte-buddy
56 | 1.10.11
57 |
58 |
59 |
60 | net.bytebuddy
61 | byte-buddy-agent
62 | 1.10.11
63 |
64 |
65 |
66 | org.projectlombok
67 | lombok
68 | 1.18.20
69 |
70 |
71 |
72 | com.ctrip.framework.apollo
73 | apollo-client
74 | 1.6.2
75 |
76 |
77 | gson
78 | com.google.code.gson
79 |
80 |
81 |
82 |
83 |
84 | ma.glasnost.orika
85 | orika-core
86 | 1.4.3
87 |
88 |
89 | slf4j-api
90 | org.slf4j
91 |
92 |
93 |
94 |
95 |
96 | com.squareup.retrofit2
97 | retrofit
98 | 2.6.3
99 |
100 |
101 | okhttp
102 | com.squareup.okhttp3
103 |
104 |
105 |
106 |
107 |
108 | com.squareup.retrofit2
109 | converter-gson
110 | 2.7.0
111 |
112 |
113 | retrofit
114 | com.squareup.retrofit2
115 |
116 |
117 |
118 |
119 |
120 | com.jakewharton.retrofit
121 | retrofit2-rxjava2-adapter
122 | 1.0.0
123 |
124 |
125 | retrofit
126 | com.squareup.retrofit2
127 |
128 |
129 | reactive-streams
130 | org.reactivestreams
131 |
132 |
133 |
134 |
135 |
136 | io.micrometer
137 | micrometer-core
138 | 1.6.5
139 |
140 |
141 |
142 | org.apache.skywalking
143 | apm-toolkit-trace
144 | 8.7.0
145 |
146 |
147 |
148 | com.jakewharton.retrofit
149 | retrofit2-reactor-adapter
150 | ${retrofit2-reactor-adapter.version}
151 |
152 |
153 | retrofit
154 | com.squareup.retrofit2
155 |
156 |
157 | reactor-core
158 | io.projectreactor
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 | org.apache.maven.plugins
168 | maven-compiler-plugin
169 |
170 | 1.8
171 | 1.8
172 |
173 |
174 |
175 |
176 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/config/ApolloConfigTool.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.config;
2 |
3 | import cn.hutool.core.collection.CollectionUtil;
4 | import com.ctrip.framework.apollo.Config;
5 | import com.ctrip.framework.apollo.ConfigService;
6 | import com.google.common.collect.Maps;
7 | import com.itkevin.common.listener.ConfigListener;
8 |
9 | import java.util.Map;
10 | import java.util.Set;
11 |
12 | public class ApolloConfigTool implements ConfigTool{
13 |
14 | @Override
15 | public Map getConfig() {
16 | // 添加apollo配置监听器,获取apollo配置放入缓存
17 | Map map = Maps.newHashMap();
18 | Config config = ConfigService.getConfig("skyeye");
19 | config.addChangeListener(new ConfigListener());
20 | Set propertyNames = config.getPropertyNames();
21 | if (!CollectionUtil.isEmpty(propertyNames)) {
22 | propertyNames.forEach(propertyName -> {
23 | String propertyValue = config.getProperty(propertyName, null);
24 | map.put(propertyName,propertyValue);
25 | });
26 | }
27 | return map;
28 | }
29 |
30 | @Override
31 | public Integer sortFlag() {
32 | return 0;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/config/ConfigTool.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.config;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * 开放接口配置
7 | */
8 | public interface ConfigTool {
9 |
10 | /**
11 | * 获取配置
12 | * @return
13 | */
14 | Map getConfig();
15 |
16 | /**
17 | * 排序过滤器,选取数据最大的作为获取配置的途径
18 | * @return
19 | */
20 | Integer sortFlag();
21 | }
22 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/config/SysConfig.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.config;
2 |
3 | import cn.hutool.json.JSONUtil;
4 | import com.itkevin.common.constants.SysConstant;
5 | import lombok.Getter;
6 | import org.apache.commons.lang3.StringUtils;
7 |
8 | import java.io.Serializable;
9 | import java.util.Map;
10 | import java.util.Objects;
11 |
12 | public class SysConfig implements Serializable {
13 |
14 | public static SysConfig instance = new SysConfig();
15 |
16 | /**
17 | * log报警开启
18 | */
19 | @Getter
20 | private Boolean alarmEnabled = false;
21 |
22 | /**
23 | * 报警机器人配置(支持多个机器人)
24 | */
25 | @Getter
26 | private String alarmTalkHook = "{}";
27 |
28 | /**
29 | * 报警严重错误机器人配置(支持多个机器人)
30 | */
31 | @Getter
32 | private String alarmSeriousTalkHook = "{}";
33 |
34 | /**
35 | * 堆栈行数配置
36 | */
37 | @Getter
38 | private Integer alarmStacknum = 10;
39 |
40 | /**
41 | * 单条报警白名单
42 | */
43 | @Getter
44 | private String alarmWhiteList = "";
45 |
46 | /**
47 | * 聚合报警白名单
48 | */
49 | @Getter
50 | private String alarmAggreWhiteList = "";
51 |
52 | /**
53 | * 报警间隔时间(单位分钟)
54 | */
55 | @Getter
56 | private Integer alarmNotifyTime = 1;
57 |
58 | /**
59 | * 报警次数阀值
60 | */
61 | @Getter
62 | private Integer alarmNotifyCount = 5;
63 |
64 | /**
65 | * 接口耗时报警间隔时间(单位分钟)
66 | */
67 | @Getter
68 | private Integer alarmUriElapsedTime = 1;
69 |
70 | /**
71 | * 接口耗时超过阀值时间的次数阀值(阀值时间如果不指定则默认1000毫秒)
72 | */
73 | @Getter
74 | private Integer alarmUriElapsedCount = 1000;
75 |
76 | /**
77 | * 指定URI接口耗时时间阀值(单位毫秒,支持指定多个URI)
78 | */
79 | @Getter
80 | private String alarmUriElapsed = "{}";
81 |
82 | /**
83 | * 指定接口耗时时间阀值(单位毫秒,全局指定,不配置默认1000毫秒)
84 | */
85 | @Getter
86 | private Long alarmUriElapsedGlobal = 1000L;
87 |
88 | /**
89 | * skyeye异常报警途径
90 | */
91 | @Getter
92 | private String alarmTool = "wework";
93 |
94 | /**
95 | * 需要监控的LogAppender名称
96 | */
97 | @Getter
98 | private String skyeyeLogAppender = "file";
99 |
100 |
101 | public static SysConfig convertMap2SysConfig(Map map){
102 | String skyeyeLogAppenderStr = map.get(SysConstant.LOG_APPENDER);
103 | if(StringUtils.isNotBlank(skyeyeLogAppenderStr)){
104 | instance.skyeyeLogAppender = skyeyeLogAppenderStr;
105 | }
106 |
107 | String enableStr = map.get(SysConstant.ALARM_ENABLED);
108 | if(!Objects.isNull(enableStr)){
109 | instance.alarmEnabled = Boolean.parseBoolean(enableStr);
110 | }
111 | String alarmTalkStr = map.get(SysConstant.ALARM_DINGTALK);
112 | if(JSONUtil.isJson(alarmTalkStr)){
113 | instance.alarmTalkHook = alarmTalkStr;
114 | }
115 |
116 | String alarmSeriousTalkStr = map.get(SysConstant.ALARM_SERIOUS_DINGTALK);
117 | if(JSONUtil.isJson(alarmSeriousTalkStr)){
118 | instance.alarmSeriousTalkHook = alarmSeriousTalkStr;
119 | }
120 |
121 | String alarmStackNum = map.get(SysConstant.ALARM_STACKNUM);
122 | if(StringUtils.isNumeric(alarmStackNum)){
123 | instance.alarmStacknum = Integer.parseInt(alarmStackNum);
124 | }
125 |
126 | String alarmWhiteList = map.get(SysConstant.ALARM_WHITE_LIST);
127 | if(StringUtils.isNotBlank(alarmWhiteList)){
128 | instance.alarmWhiteList = alarmWhiteList;
129 | }
130 | String alarmAggreWhiteList = map.get(SysConstant.ALARM_AGGRE_WHITE_LIST);
131 | if(StringUtils.isNotBlank(alarmAggreWhiteList)){
132 | instance.alarmAggreWhiteList = alarmAggreWhiteList;
133 | }
134 | String alarmNotifyTime = map.get(SysConstant.ALARM_NOTIFY_TIME);
135 | if(StringUtils.isNumeric(alarmNotifyTime)){
136 | instance.alarmNotifyTime = Integer.parseInt(alarmNotifyTime);
137 | }
138 | String alarmNotifyCount = map.get(SysConstant.ALARM_NOTIFY_COUNT);
139 | if(StringUtils.isNumeric(alarmNotifyCount)){
140 | instance.alarmNotifyCount = Integer.parseInt(alarmNotifyCount);
141 | }
142 | String alarmUriElapsedTime = map.get(SysConstant.ALARM_URI_ELAPSED_TIME);
143 | if(StringUtils.isNumeric(alarmUriElapsedTime)){
144 | instance.alarmUriElapsedTime = Integer.parseInt(alarmUriElapsedTime);
145 | }
146 | String alarmUriElapsedCount = map.get(SysConstant.ALARM_URI_ELAPSED_COUNT);
147 | if(StringUtils.isNumeric(alarmUriElapsedCount)){
148 | instance.alarmUriElapsedCount = Integer.parseInt(alarmUriElapsedCount);
149 | }
150 | String alarmUriElapsed = map.get(SysConstant.ALARM_URI_ELAPSED);
151 | if(JSONUtil.isJson(alarmUriElapsed)){
152 | instance.alarmUriElapsed = alarmUriElapsed;
153 | }
154 | String alarmUriElapsedGlobal = map.get(SysConstant.ALARM_URI_ELAPSED_GLOBAL);
155 | if(StringUtils.isNumeric(alarmUriElapsedGlobal)){
156 | instance.alarmUriElapsedGlobal = Long.parseLong(alarmUriElapsedGlobal);
157 | }
158 |
159 | String alarmTool = map.get(SysConstant.ALARM_TOOL);
160 | if(StringUtils.isNotBlank(alarmTool)){
161 | instance.alarmTool = alarmTool;
162 | }
163 | return instance;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/constants/SysConstant.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.constants;
2 |
3 | /**
4 | * 常量类
5 | */
6 | public class SysConstant {
7 |
8 | /**
9 | * 欢迎语
10 | */
11 | public static final String WELCOME = "Welcome to the skyeye";
12 |
13 | /**
14 | * log报警开启
15 | */
16 | public static final String ALARM_ENABLED = "skyeye.log.alarm.enabled";
17 |
18 | /**
19 | * 报警机器人配置(支持多个机器人)
20 | */
21 | public static final String ALARM_DINGTALK = "skyeye.log.alarm.talk.hook";
22 |
23 | /**
24 | * 报警严重错误机器人配置(支持多个机器人)
25 | */
26 | public static final String ALARM_SERIOUS_DINGTALK = "skyeye.log.alarm.serious.talk.hook";
27 |
28 | /**
29 | * 异常message输出默认长度
30 | */
31 | public static final int ALARM_MESSAGE_STR_DEFAULT = 500;
32 |
33 | /**
34 | * 异常堆栈输出默认行数
35 | */
36 | public static final int ALARM_STACKNUM_DEFAULT = 10;
37 |
38 | /**
39 | * 堆栈行数配置
40 | */
41 | public static final String ALARM_STACKNUM = "skyeye.log.alarm.stackNum";
42 |
43 | /**
44 | * 单条报警白名单
45 | */
46 | public static final String ALARM_WHITE_LIST = "skyeye.log.alarm.white.list";
47 |
48 | /**
49 | * 聚合报警白名单
50 | */
51 | public static final String ALARM_AGGRE_WHITE_LIST = "skyeye.log.alarm.aggre.white.list";
52 |
53 | /**
54 | * 报警间隔时间(单位分钟)
55 | */
56 | public static final String ALARM_NOTIFY_TIME = "skyeye.log.alarm.notify.time";
57 |
58 | /**
59 | * 报警次数阀值
60 | */
61 | public static final String ALARM_NOTIFY_COUNT = "skyeye.log.alarm.notify.count";
62 |
63 | /**
64 | * 报警间隔时间默认值(单位分钟)
65 | */
66 | public static final Integer ALARM_NOTIFY_TIME_DEFAULT = 1;
67 |
68 | /**
69 | * 报警次数阀值默认值
70 | */
71 | public static final Integer ALARM_NOTIFY_COUNT_DEFAULT = 5;
72 |
73 | /**
74 | * 接口耗时报警间隔时间(单位分钟)
75 | */
76 | public static final String ALARM_URI_ELAPSED_TIME = "skyeye.log.alarm.uri.elapsed.time";
77 |
78 | /**
79 | * 接口耗时超过阀值时间的次数阀值(阀值时间如果不指定则默认1000毫秒)
80 | */
81 | public static final String ALARM_URI_ELAPSED_COUNT = "skyeye.log.alarm.uri.elapsed.count";
82 |
83 | /**
84 | * 指定URI接口耗时时间阀值(单位毫秒,支持指定多个URI)
85 | */
86 | public static final String ALARM_URI_ELAPSED = "skyeye.log.alarm.uri.elapsed";
87 |
88 | /**
89 | * 指定接口耗时时间阀值(单位毫秒,全局指定,不配置默认1000毫秒)
90 | */
91 | public static final String ALARM_URI_ELAPSED_GLOBAL = "skyeye.log.alarm.uri.elapsed.global";
92 |
93 | /**
94 | * 接口耗时阀值时间默认值(单位毫秒)
95 | */
96 | public static final long ALARM_URI_ELAPSED_DEFAULT = 1000;
97 |
98 | /**
99 | * 自定义线程池核心线程数
100 | */
101 | public static final Integer THREAD_NUM = 5;
102 |
103 | /**
104 | * 自定义线程池最大线程数
105 | */
106 | public static final Integer MAX_THREAD_NUM = 20;
107 |
108 | /**
109 | * 监控报警指标名称
110 | */
111 | public static final String ALARM_METRIC_NAME = "skyeye_alarm_num_gauge";
112 |
113 | /**
114 | * 监控报警指标名称help
115 | */
116 | public static final String ALARM_METRIC_NAME_HELP = "skyeye异常报警数";
117 |
118 | /**
119 | * skyeye异常报警途径
120 | */
121 | public static final String ALARM_TOOL = "skyeye.log.alarm.tool";
122 |
123 | /**
124 | * 默认报警途径为企业微信
125 | */
126 | public static final String ALARM_TOOL_DEFAULT = "wework";
127 |
128 | /**
129 | * 需要监控的日志appender
130 | */
131 | public static final String LOG_APPENDER = "skyeye.log.appender";
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/enums/AlarmToolEnum.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.enums;
2 |
3 | import com.itkevin.common.notice.NoticeInterface;
4 | import com.itkevin.common.notice.dingding.DingTalkNotice;
5 | import com.itkevin.common.notice.workwx.WorkWeiXinTalkNotice;
6 |
7 | import java.util.Arrays;
8 |
9 | public enum AlarmToolEnum {
10 |
11 | WEWORK("wework", WorkWeiXinTalkNotice.class),
12 |
13 | DINGDING("dingding", DingTalkNotice.class);
14 |
15 | private String code;
16 |
17 | private Class name;
18 |
19 | public String getCode() {
20 | return code;
21 | }
22 |
23 | public Class getName() {
24 | return name;
25 | }
26 |
27 | AlarmToolEnum(String code, Class name) {
28 | this.code = code;
29 | this.name = name;
30 | }
31 |
32 | public static AlarmToolEnum getByValue(String code) {
33 | return Arrays.stream(AlarmToolEnum.values())
34 | .filter(resultCodeEnum -> resultCodeEnum.code.equals(code))
35 | .findFirst().orElse(null);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/enums/BusinessTypeEnum.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.enums;
2 |
3 | /**
4 | * 业务类型枚举
5 | */
6 | public enum BusinessTypeEnum {
7 |
8 | NOTIFY, URI_ELAPSED
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/enums/LogConstantEnum.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.enums;
2 |
3 | import java.util.Arrays;
4 |
5 | /**
6 | * 日志常量枚举
7 | */
8 | public enum LogConstantEnum {
9 |
10 | REQUEST_URI("requestURI", "请求URI"),
11 |
12 | EXCEPTION_MESSAGE("exceptionMessage", "异常信息"),
13 |
14 | ERROR_MESSAGE("errorMessage", "error信息"),
15 |
16 | ALARM_TIME("alarmTime", "时间"),
17 |
18 | ALARM_COUNT("alarmCount", "次数"),
19 |
20 | URI_ELAPSED_THRESHOLD("uriElapsedThreshold", "阀值"),
21 |
22 | URI_ELAPSED_TRACEID_LIST("uriElapsedTraceidList", "抽样traceId"),
23 |
24 | MAX_URI_ELAPSED("maxUriElapsed", "最大耗时"),
25 |
26 | MAX_URI_ELAPSED_TRACEID("maxUriElapsedTraceId", "最大耗时traceId"),
27 |
28 | SERVER_NAME("serverName", "服务名称"),
29 |
30 | SERVER_IP("serverIP", "服务器IP"),
31 |
32 | SERVER_HOSTNAME("serverHostname", "服务器hostname");
33 |
34 | private String code;
35 | private String name;
36 |
37 | public String getCode() {
38 | return code;
39 | }
40 |
41 | public String getName() {
42 | return name;
43 | }
44 |
45 | LogConstantEnum(String code, String name) {
46 | this.code = code;
47 | this.name = name;
48 | }
49 |
50 | public static LogConstantEnum getByValue(String code) {
51 | return Arrays.stream(LogConstantEnum.values())
52 | .filter(resultCodeEnum -> resultCodeEnum.code.equals(code))
53 | .findFirst().orElse(null);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/enums/LogLevelEnum.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.enums;
2 |
3 | public enum LogLevelEnum {
4 |
5 | NORMAL, SERIOUS
6 | }
7 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/enums/LogTypeEnum.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.enums;
2 |
3 | /**
4 | * 日志类型枚举
5 | */
6 | public enum LogTypeEnum {
7 |
8 | LOG4J, LOGBACK
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/enums/MDCConstantEnum.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.enums;
2 |
3 | import java.util.Arrays;
4 |
5 | /**
6 | * MDC常量枚举
7 | */
8 | public enum MDCConstantEnum {
9 |
10 | ERROR_MESSAGE("errorMessage", "error信息"),
11 | SERVER_NAME("serverName", "服务名称"),
12 | SOURCE_IP("sourceIP", "来源IP"),
13 | SERVER_IP("serverIP", "服务器IP"),
14 | SERVER_HOSTNAME("serverHostname", "服务器hostname"),
15 | OCCURRENCE_TIME("occurrenceTime", "发生时间"),
16 | REQUEST_TYPE("requestType", "请求类型"),
17 | TRACE_ID("traceId", "跟踪traceId"),
18 | REQUEST_URI("requestURI", "请求URI"),
19 | REQUEST_PARAM("requestParam", "请求参数"),
20 | MESSAGE_TOPIC("messageTopic", "消息topic"),
21 | MESSAGE_ID("messageId", "消息msgId"),
22 | MESSAGE_KEYS("messageKeys", "消息keys"),
23 | EVENT_NAME("eventName", "事件name"),
24 | EVENT_PAYLOAD("eventPayload", "事件payload"),
25 | EXCEPTION_MESSAGE("exceptionMessage", "异常信息"),
26 | EXCEPTION_STACKTRACE("exceptionStackTrace", "异常堆栈"),
27 | DEPLOY_TAG("deployTag", "部署tag号");
28 |
29 | private String code;
30 | private String name;
31 |
32 | public String getCode() {
33 | return code;
34 | }
35 |
36 | public String getName() {
37 | return name;
38 | }
39 |
40 | MDCConstantEnum(String code, String name) {
41 | this.code = code;
42 | this.name = name;
43 | }
44 |
45 | public static MDCConstantEnum getByValue(String code) {
46 | return Arrays.stream(MDCConstantEnum.values())
47 | .filter(resultCodeEnum -> resultCodeEnum.code.equals(code))
48 | .findFirst().orElse(null);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/enums/RequestTypeEnum.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.enums;
2 |
3 | /**
4 | * 请求类型枚举
5 | */
6 | public enum RequestTypeEnum {
7 |
8 | HTTP, DUBBO, MQ, JOB, EVENT
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/listener/ConfigListener.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.listener;
2 |
3 | import com.ctrip.framework.apollo.ConfigChangeListener;
4 | import com.ctrip.framework.apollo.enums.PropertyChangeType;
5 | import com.ctrip.framework.apollo.model.ConfigChange;
6 | import com.ctrip.framework.apollo.model.ConfigChangeEvent;
7 | import com.google.common.collect.Maps;
8 | import com.itkevin.common.config.SysConfig;
9 | import lombok.extern.slf4j.Slf4j;
10 | import org.apache.commons.lang3.StringUtils;
11 |
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | @Slf4j
16 | public class ConfigListener implements ConfigChangeListener {
17 |
18 | @Override
19 | public void onChange(ConfigChangeEvent configChangeEvent) {
20 | try {
21 | for (String key : configChangeEvent.changedKeys()) {
22 | ConfigChange change = configChangeEvent.getChange(key);
23 | PropertyChangeType changeType = change.getChangeType();
24 | String propertyName = change.getPropertyName();
25 | String oldValue = change.getOldValue();
26 | String newValue = change.getNewValue();
27 | if(StringUtils.isBlank(propertyName)){
28 | continue;
29 | }
30 | log.info("log skyeye >>> ConfigListener changeType: {}, propertyName: {}, oldValue: {}, newValue: {}", changeType.name(), propertyName, oldValue, newValue);
31 | if(!PropertyChangeType.DELETED.equals(changeType)){
32 | Map map = Maps.newHashMap();
33 | map.put(propertyName,newValue);
34 | SysConfig.convertMap2SysConfig(map);
35 | }
36 | }
37 | } catch (Exception e) {
38 | log.warn("log skyeye >>> ConfigListener.onChange occur exception", e);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/model/BusinessData.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.model;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 |
7 | @Data
8 | public class BusinessData implements Serializable {
9 | private static final long serialVersionUID = 1L;
10 |
11 | /**
12 | * 请求URI
13 | */
14 | private String requestURI;
15 |
16 | /**
17 | * 异常信息
18 | */
19 | private String exceptionMessage;
20 |
21 | /**
22 | * error信息
23 | */
24 | private String errorMessage;
25 |
26 | /**
27 | * 服务名称
28 | */
29 | private String serverName;
30 |
31 | /**
32 | * 服务器IP
33 | */
34 | private String serverIP;
35 |
36 | /**
37 | * 服务器hostname
38 | */
39 | private String serverHostname;
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/model/FilterMessage.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.model;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 |
7 | @Data
8 | public class FilterMessage implements Serializable {
9 | private static final long serialVersionUID = 1L;
10 |
11 | /**
12 | * 日志类型
13 | */
14 | private String logType;
15 |
16 | /**
17 | * error信息
18 | */
19 | private String msg;
20 |
21 | /**
22 | * 异常message
23 | */
24 | private String message;
25 |
26 | /**
27 | * 异常堆栈
28 | */
29 | private String stackTrace;
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/model/HashedWheelData.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.model;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | @Data
8 | @NoArgsConstructor
9 | @AllArgsConstructor
10 | public class HashedWheelData {
11 | private static final long serialVersionUID = 1L;
12 |
13 | /**
14 | * 延迟时间(单位分钟)
15 | */
16 | private Integer delayTime;
17 |
18 | /**
19 | * 业务类型
20 | */
21 | private String businessType;
22 |
23 | /**
24 | * 业务id
25 | */
26 | private String businessId;
27 |
28 | /**
29 | * 业务数据
30 | */
31 | private String businessData;
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/model/LogCompressData.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.model;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 | import java.util.List;
7 |
8 | @Data
9 | public class LogCompressData implements Serializable {
10 | private static final long serialVersionUID = 1L;
11 |
12 | /**
13 | * 请求URI
14 | */
15 | private String requestURI;
16 |
17 | /**
18 | * 异常信息
19 | */
20 | private String exceptionMessage;
21 |
22 | /**
23 | * error信息
24 | */
25 | private String errorMessage;
26 |
27 | /**
28 | * 报警间隔
29 | */
30 | private Integer alarmTime;
31 |
32 | /**
33 | * 报警次数
34 | */
35 | private Integer alarmCount;
36 |
37 | /**
38 | * traceId集合
39 | */
40 | private List traceIdList;
41 |
42 | /**
43 | * 接口耗时时间阀值
44 | */
45 | private Long uriElapsedThreshold;
46 |
47 | /**
48 | * 接口最大耗时时间
49 | */
50 | private Long maxUriElapsed;
51 |
52 | /**
53 | * 接口最大耗时traceId
54 | */
55 | private String maxUriElapsedTraceId;
56 |
57 | /**
58 | * 服务名称
59 | */
60 | private String serverName;
61 |
62 | /**
63 | * 服务器IP
64 | */
65 | private String serverIP;
66 |
67 | /**
68 | * 服务器hostname
69 | */
70 | private String serverHostname;
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/model/LogData.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.model;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 |
7 | @Data
8 | public class LogData implements Serializable {
9 | private static final long serialVersionUID = 1L;
10 |
11 | /**
12 | * 是否过滤日志数据
13 | */
14 | private Boolean filter;
15 |
16 | /**
17 | * 日志级别
18 | */
19 | private String level;
20 |
21 | /**
22 | * error信息
23 | */
24 | private String errorMessage;
25 |
26 | /**
27 | * 服务名称
28 | */
29 | private String serverName;
30 |
31 | /**
32 | * 来源IP
33 | */
34 | private String sourceIP;
35 |
36 | /**
37 | * 服务器IP
38 | */
39 | private String serverIP;
40 |
41 | /**
42 | * 服务器hostname
43 | */
44 | private String serverHostname;
45 |
46 | /**
47 | * 发生时间
48 | */
49 | private String occurrenceTime;
50 |
51 | /**
52 | * 请求类型
53 | */
54 | private String requestType;
55 |
56 | /**
57 | * 跟踪traceId
58 | */
59 | private String traceId;
60 |
61 | /**
62 | * 请求URI
63 | */
64 | private String requestURI;
65 |
66 | /**
67 | * 请求参数
68 | */
69 | private String requestParam;
70 |
71 | /**
72 | * 消息topic
73 | */
74 | private String messageTopic;
75 |
76 | /**
77 | * 消息msgId
78 | */
79 | private String messageId;
80 |
81 | /**
82 | * 消息keys
83 | */
84 | private String messageKeys;
85 |
86 | /**
87 | * 事件name
88 | */
89 | private String eventName;
90 |
91 | /**
92 | * 事件payload
93 | */
94 | private String eventPayload;
95 |
96 | /**
97 | * 异常信息
98 | */
99 | private String exceptionMessage;
100 |
101 | /**
102 | * 异常堆栈
103 | */
104 | private String exceptionStackTrace;
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/model/LogUriElapsedData.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.model;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 | import java.util.List;
7 |
8 | @Data
9 | public class LogUriElapsedData implements Serializable {
10 | private static final long serialVersionUID = 1L;
11 |
12 | /**
13 | * 请求URI
14 | */
15 | private String requestURI;
16 |
17 | /**
18 | * 报警间隔
19 | */
20 | private Integer alarmTime;
21 |
22 | /**
23 | * 报警次数
24 | */
25 | private Integer alarmCount;
26 |
27 | /**
28 | * 阀值
29 | */
30 | private Long uriElapsedThreshold;
31 |
32 | /**
33 | * 抽样traceId
34 | */
35 | private List traceIdList;
36 |
37 | /**
38 | * 最大耗时
39 | */
40 | private Long maxUriElapsed;
41 |
42 | /**
43 | * 最大耗时traceId
44 | */
45 | private String maxUriElapsedTraceId;
46 |
47 | /**
48 | * 服务名称
49 | */
50 | private String serverName;
51 |
52 | /**
53 | * 服务器IP
54 | */
55 | private String serverIP;
56 |
57 | /**
58 | * 服务器hostname
59 | */
60 | private String serverHostname;
61 | }
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/model/TaskModel.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.model;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 |
7 | @Data
8 | public class TaskModel implements Serializable {
9 | private static final long serialVersionUID = 1L;
10 |
11 | /**
12 | * 业务id
13 | */
14 | private String businessId;
15 |
16 | /**
17 | * 循环次数
18 | */
19 | private Integer cycleNum;
20 | }
21 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/model/UriElapsedCollect.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.model;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 |
7 | @Data
8 | public class UriElapsedCollect implements Serializable {
9 | private static final long serialVersionUID = 1L;
10 |
11 | /**
12 | * 请求URI
13 | */
14 | private String requestURI;
15 |
16 | /**
17 | * URI耗时
18 | */
19 | private long elapsed;
20 |
21 | /**
22 | * 跟踪traceId
23 | */
24 | private String traceId;
25 |
26 | /**
27 | * 服务名称
28 | */
29 | private String serverName;
30 |
31 | /**
32 | * 服务器IP
33 | */
34 | private String serverIP;
35 |
36 | /**
37 | * 服务器hostname
38 | */
39 | private String serverHostname;
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/model/UriElapsedData.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.model;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 |
7 | @Data
8 | public class UriElapsedData implements Serializable {
9 | private static final long serialVersionUID = 1L;
10 |
11 | /**
12 | * 请求uri
13 | */
14 | private String uri;
15 |
16 | /**
17 | * 耗时时间(单位毫秒)
18 | */
19 | private long elapsed;
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/notice/MarkDownBaseMessage.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.notice;
2 |
3 | import cn.hutool.json.JSONObject;
4 | import cn.hutool.json.JSONUtil;
5 | import com.itkevin.common.notice.model.BaseMessage;
6 | import lombok.Data;
7 | import lombok.EqualsAndHashCode;
8 |
9 | import java.io.Serializable;
10 |
11 | @EqualsAndHashCode(callSuper = true)
12 | @Data
13 | public class MarkDownBaseMessage extends BaseMessage implements Serializable {
14 | private static final long serialVersionUID = 1L;
15 |
16 | /**
17 | * 消息类型
18 | */
19 | private String msgType = "markdown";
20 |
21 | /**
22 | * 消息标题
23 | */
24 | private String title;
25 |
26 | /**
27 | * 消息内容
28 | */
29 | private String content;
30 |
31 | @Override
32 | public String toString() {
33 | JSONObject markdownContent = new JSONObject();
34 | markdownContent.put("title", this.getTitle());
35 | markdownContent.put("content", this.getContent());
36 | JSONObject json = new JSONObject();
37 | json.put("msgtype", this.getMsgType());
38 | json.put("at", this.setAtAllAndMobile(this.getAtMobiles()));
39 | json.put("markdown", markdownContent);
40 | return JSONUtil.toJsonStr(json);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/notice/NoticeInterface.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.notice;
2 |
3 | import okhttp3.OkHttpClient;
4 |
5 | import java.util.concurrent.TimeUnit;
6 |
7 | /**
8 | * 开放通知接口
9 | */
10 | public interface NoticeInterface {
11 |
12 | /**
13 | * 发送消息
14 | * @param baseMessage
15 | */
16 | void sendMessage(MarkDownBaseMessage baseMessage);
17 |
18 | /**
19 | * 配置过滤器
20 | * @return
21 | */
22 | String filterFlag();
23 |
24 | /**
25 | * 初始化OkHttpClient实例
26 | * @return
27 | */
28 | static OkHttpClient initOkHttpClient() {
29 | return new OkHttpClient.Builder()
30 | .retryOnConnectionFailure(true)
31 | .connectTimeout(500, TimeUnit.MILLISECONDS)
32 | .readTimeout(500, TimeUnit.MILLISECONDS)
33 | .writeTimeout(500, TimeUnit.MILLISECONDS)
34 | .build();
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/notice/dingding/DingConfigData.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.notice.dingding;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 |
7 | @Data
8 | public class DingConfigData implements Serializable {
9 | private static final long serialVersionUID = 1L;
10 |
11 | /**
12 | * webHook
13 | */
14 | private String webHook;
15 |
16 | /**
17 | * token
18 | */
19 | private String accessToken;
20 |
21 | /**
22 | * secret
23 | */
24 | private String secret;
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/notice/dingding/DingMarkDownMessage.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.notice.dingding;
2 |
3 | import cn.hutool.json.JSONObject;
4 | import cn.hutool.json.JSONUtil;
5 | import com.itkevin.common.notice.MarkDownBaseMessage;
6 | import com.itkevin.common.notice.model.BaseMessage;
7 | import lombok.Data;
8 | import lombok.EqualsAndHashCode;
9 |
10 | import java.io.Serializable;
11 |
12 | @EqualsAndHashCode(callSuper = true)
13 | @Data
14 | public class DingMarkDownMessage extends MarkDownBaseMessage implements Serializable {
15 | private static final long serialVersionUID = 1L;
16 |
17 | /**
18 | * 消息类型
19 | */
20 | private String msgType = "markdown";
21 |
22 | /**
23 | * 消息标题
24 | */
25 | private String title;
26 |
27 | /**
28 | * 消息内容
29 | */
30 | private String content;
31 |
32 | @Override
33 | public String toString() {
34 | JSONObject markdownContent = new JSONObject();
35 | markdownContent.put("title", this.getTitle());
36 | markdownContent.put("text", this.getContent());
37 | JSONObject json = new JSONObject();
38 | json.put("msgtype", this.getMsgType());
39 | json.put("at", this.setAtAllAndMobile(this.getAtMobiles()));
40 | json.put("markdown", markdownContent);
41 | return JSONUtil.toJsonStr(json);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/notice/dingding/DingTalkNotice.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.notice.dingding;
2 |
3 | import cn.hutool.core.util.CharsetUtil;
4 | import cn.hutool.http.HttpUtil;
5 | import cn.hutool.json.JSONObject;
6 | import cn.hutool.json.JSONUtil;
7 | import com.itkevin.common.config.SysConfig;
8 | import com.itkevin.common.enums.LogLevelEnum;
9 | import com.itkevin.common.notice.MarkDownBaseMessage;
10 | import com.itkevin.common.notice.NoticeInterface;
11 | import com.itkevin.common.util.LocalCacheUtils;
12 | import com.itkevin.common.util.StringConverterFactory;
13 | import com.jakewharton.retrofit2.adapter.reactor.ReactorCallAdapterFactory;
14 | import lombok.extern.slf4j.Slf4j;
15 | import okhttp3.MediaType;
16 | import okhttp3.RequestBody;
17 | import okhttp3.ResponseBody;
18 | import org.apache.commons.lang3.StringUtils;
19 | import reactor.core.publisher.Mono;
20 | import retrofit2.Response;
21 | import retrofit2.Retrofit;
22 | import retrofit2.converter.gson.GsonConverterFactory;
23 |
24 | import javax.crypto.Mac;
25 | import javax.crypto.spec.SecretKeySpec;
26 | import java.net.URLEncoder;
27 | import java.util.Base64;
28 | import java.util.List;
29 | import java.util.Map;
30 |
31 | @Slf4j
32 | public class DingTalkNotice implements NoticeInterface {
33 |
34 | private static volatile DingTalkNotice dingTalkNotice;
35 |
36 | public static DingTalkNotice getInstance() {
37 | try {
38 | if (null == dingTalkNotice) {
39 | synchronized (DingTalkNotice.class) {
40 | if (null == dingTalkNotice) {
41 | dingTalkNotice = new DingTalkNotice();
42 | }
43 | }
44 | }
45 | } catch (Exception e) {
46 | e.printStackTrace();
47 | }
48 | return dingTalkNotice;
49 | }
50 |
51 | /**
52 | * 钉钉服务接口(retrofit2-reactor-adapter:https://github.com/JakeWharton/retrofit2-reactor-adapter)
53 | */
54 | private static DingTalkService dingTalkService = new Retrofit.Builder()
55 | .baseUrl("https://oapi.dingtalk.com")
56 | .client(NoticeInterface.initOkHttpClient())
57 | .addConverterFactory(new StringConverterFactory())
58 | .addConverterFactory(GsonConverterFactory.create())
59 | .addCallAdapterFactory(ReactorCallAdapterFactory.create())
60 | .build()
61 | .create(DingTalkService.class);
62 |
63 | /**
64 | * 钉钉机器人标识
65 | */
66 | private static final String WEBHOOK_INDEX = "DINGDING_WEBHOOK_INDEX";
67 |
68 | /**
69 | * 发送钉钉消息
70 | *
71 | * @param dingMarkDownMessage
72 | */
73 | @Override
74 | public void sendMessage(MarkDownBaseMessage dingMarkDownMessage) {
75 | try {
76 | String level = dingMarkDownMessage.getLevel();
77 | String alarmDingTalk = StringUtils.isNotBlank(level) && level.equals(LogLevelEnum.SERIOUS.name())
78 | ? SysConfig.instance.getAlarmSeriousTalkHook()
79 | : SysConfig.instance.getAlarmTalkHook();
80 | if (StringUtils.isBlank(alarmDingTalk)) {
81 | log.warn("log skyeye >>> config 'skyeye.log.alarm.dingtalk' or 'skyeye.log.alarm.serious.dingtalk' is null");
82 | return;
83 | }
84 | List dingConfigDataList = JSONUtil.toList(JSONUtil.parseArray(alarmDingTalk), DingConfigData.class);
85 | String str = converMarkDownDingMessage2Str(dingMarkDownMessage);
86 | send(dingConfigDataList, str);
87 | } catch (Exception e) {
88 | log.warn("log skyeye >>> DingTalkUtils.sendMessage occur exception", e);
89 | }
90 | }
91 |
92 | private String converMarkDownDingMessage2Str(MarkDownBaseMessage markDownBaseMessage) {
93 | JSONObject markdownContent = new JSONObject();
94 | markdownContent.put("title", markDownBaseMessage.getTitle());
95 | markdownContent.put("text", markDownBaseMessage.getContent());
96 | JSONObject json = new JSONObject();
97 | json.put("msgtype", markDownBaseMessage.getMsgType());
98 | json.put("at", markDownBaseMessage.setAtAllAndMobile(markDownBaseMessage.getAtMobiles()));
99 | json.put("markdown", markdownContent);
100 | return JSONUtil.toJsonStr(json);
101 | }
102 |
103 | /**
104 | * 发送
105 | *
106 | * @param dingConfigDataList
107 | * @param jsonContent
108 | */
109 | private void send(List dingConfigDataList, String jsonContent) {
110 | int currentIndex = getIndex(dingConfigDataList.size());
111 | DingConfigData dingConfigData = dingConfigDataList.get(currentIndex);
112 | String accessToken = dingConfigData.getAccessToken();
113 | if (StringUtils.isBlank(accessToken)) {
114 | Map paramMap = HttpUtil.decodeParamMap(dingConfigData.getWebHook(), CharsetUtil.defaultCharset());
115 | accessToken = paramMap.get("access_token");
116 | accessToken = StringUtils.isBlank(accessToken) ? paramMap.get("accessToken") : accessToken;
117 | }
118 | send(accessToken, dingConfigData.getSecret(), jsonContent);
119 | }
120 |
121 | /**
122 | * 发送
123 | *
124 | * @param accessToken
125 | * @param secret
126 | * @param jsonContent
127 | */
128 | private boolean send(String accessToken, String secret, String jsonContent) {
129 | try {
130 | Long timestamp = System.currentTimeMillis();
131 | String sign = StringUtils.isNotBlank(secret) ? signData(timestamp, secret) : "";
132 | RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), jsonContent);
133 | log.info("log skyeye >>> send dingding accessToken: {}, timestamp: {}, sign: {}", accessToken, timestamp, sign);
134 | Mono> mono = dingTalkService.robotSendMono(accessToken, timestamp, sign, requestBody);
135 | ResponseBody responseBody = mono.blockOptional().map(Response::body).orElse(null);
136 | String string = responseBody != null ? responseBody.string() : "";
137 | log.info("log skyeye >>> send dingding result: {}", string);
138 | if (string.contains("")) {
139 | return false;
140 | }
141 | JSONObject result = JSONUtil.parseObj(string);
142 | if (result.get("errcode") == null || !"0".equals(String.valueOf(result.get("errcode")))) {
143 | return false;
144 | }
145 | return true;
146 | } catch (Exception e) {
147 | log.warn("log skyeye >>> DingTalkUtils.send occur exception", e);
148 | return false;
149 | }
150 | }
151 |
152 | /**
153 | * 验签
154 | *
155 | * @param timestamp
156 | * @param secret
157 | * @return
158 | */
159 | private String signData(Long timestamp, String secret) {
160 | try {
161 | String stringToSign = timestamp + "\n" + secret;
162 | Mac mac = Mac.getInstance("HmacSHA256");
163 | mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
164 | byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
165 | return URLEncoder.encode(new String(Base64.getEncoder().encode(signData)), "UTF-8");
166 | } catch (Exception e) {
167 | log.warn("log skyeye >>> DingTalkUtils.signData occur exception", e);
168 | return null;
169 | }
170 | }
171 |
172 |
173 | /**
174 | * 获取第几个机器人
175 | *
176 | * @param totalCount
177 | * @return
178 | */
179 | private synchronized int getIndex(int totalCount) {
180 | int currentIndex = LocalCacheUtils.getIntCache(WEBHOOK_INDEX);
181 | if (currentIndex > totalCount - 1) {
182 | currentIndex = 0;
183 | LocalCacheUtils.putIntCache(WEBHOOK_INDEX, currentIndex);
184 | }
185 | LocalCacheUtils.incr(WEBHOOK_INDEX);
186 | return currentIndex;
187 | }
188 |
189 | public static void main(String[] args) {
190 | // List dingConfigDataList = new ArrayList<>();
191 | // DingConfigData dingConfigData1= new DingConfigData();
192 | // dingConfigData1.setWebHook("https://oapi.dingtalk.com/robot/send?access_token=xxxx");
193 | // dingConfigData1.setSecret("SEC2e6249a1bf419db8a89643ebf2625dbc0c3e47af12ce130a6582415d3f38da05");
194 | // DingConfigData dingConfigData2= new DingConfigData();
195 | // dingConfigData2.setWebHook("https://oapi.dingtalk.com/robot/send?access_token=xxxx");
196 | // dingConfigData2.setSecret("SECc193b8c3b7e47ad0fd4821bf26f08dc777010ecef372689604922b1c78e8184c");
197 | // dingConfigDataList.add(dingConfigData1);
198 | // dingConfigDataList.add(dingConfigData2);
199 |
200 | // DingMarkDownMessage message = new DingMarkDownMessage();
201 | // message.setTitle("出错啦!");
202 | // String exceptionMessage;
203 | // String exceptionStackTrace;
204 | // try {
205 | // throw new RuntimeException("异常message");
206 | // } catch (Exception e) {
207 | // exceptionMessage = e.getMessage();
208 | // exceptionStackTrace = ExceptionUtils.getStackTrace(e);
209 | // }
210 | // String content = "## **" + MDCConstantEnum.ERROR_MESSAGE.getName() + ":" + "出错啦!" + "**" + System.getProperty("line.separator") +
211 | // "+ " + MDCConstantEnum.SERVER_NAME.getName() + ":" + "localhost" + System.getProperty("line.separator") +
212 | // "+ " + MDCConstantEnum.SOURCE_IP.getName() + ":" + "10.155.8.91" + System.getProperty("line.separator") +
213 | // "+ " + MDCConstantEnum.SERVER_IP.getName() + ":" + "10.155.8.91" + System.getProperty("line.separator") +
214 | // "+ " + MDCConstantEnum.SERVER_HOSTNAME.getName() + ":" + "itkevin.it.com" + System.getProperty("line.separator") +
215 | // "+ " + MDCConstantEnum.OCCURRENCE_TIME.getName() + ":" + "2020-06-23 14:20:05" + System.getProperty("line.separator") +
216 | // "+ " + MDCConstantEnum.REQUEST_TYPE.getName() + ":" + "http GET" + System.getProperty("line.separator") +
217 | // "+ " + MDCConstantEnum.TRACE_ID.getName() + ":" + "4696.167.15928932046440001" + System.getProperty("line.separator") +
218 | // "+ " + MDCConstantEnum.REQUEST_URI.getName() + ":" + "/api/health" + System.getProperty("line.separator") +
219 | // "+ " + MDCConstantEnum.REQUEST_PARAM.getName() + ":" + "{a=a, b=b}" + System.getProperty("line.separator") +
220 | // "+ " + MDCConstantEnum.EXCEPTION_MESSAGE.getName() + ":" + exceptionMessage + System.getProperty("line.separator") +
221 | // "+ " + MDCConstantEnum.EXCEPTION_STACKTRACE.getName() + ":" + System.getProperty("line.separator") + System.getProperty("line.separator") +
222 | // "`" + exceptionStackTrace + "`";
223 | // message.setContent(content);
224 | // DingTalkNotice.sendMessage(message);
225 | }
226 |
227 | @Override
228 | public String filterFlag() {
229 | return "dingding";
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/notice/dingding/DingTalkService.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.notice.dingding;
2 |
3 | import okhttp3.RequestBody;
4 | import okhttp3.ResponseBody;
5 | import reactor.core.publisher.Mono;
6 | import retrofit2.Call;
7 | import retrofit2.Response;
8 | import retrofit2.http.Body;
9 | import retrofit2.http.Headers;
10 | import retrofit2.http.POST;
11 | import retrofit2.http.Query;
12 |
13 | /**
14 | * 钉钉服务接口
15 | */
16 | public interface DingTalkService {
17 |
18 | /**
19 | * 发送钉钉消息
20 | * @param accessToken
21 | * @param timestamp
22 | * @param sign
23 | * @param body
24 | * @return
25 | */
26 | @Headers({"Content-Type: application/json; charset=utf-8"})
27 | @POST("/robot/send")
28 | Call robotSendCall(@Query("access_token") String accessToken, @Query("timestamp") Long timestamp, @Query("sign") String sign, @Body RequestBody body);
29 |
30 | /**
31 | * 发送钉钉消息
32 | * @param accessToken
33 | * @param timestamp
34 | * @param sign
35 | * @param body
36 | * @return
37 | */
38 | @Headers({"Content-Type: application/json; charset=utf-8"})
39 | @POST("/robot/send")
40 | Mono> robotSendMonoStr(@Query("access_token") String accessToken, @Query("timestamp") Long timestamp, @Query("sign") String sign, @Body RequestBody body);
41 |
42 | /**
43 | * 发送钉钉消息
44 | * @param accessToken
45 | * @param timestamp
46 | * @param sign
47 | * @param body
48 | * @return
49 | */
50 | @Headers({"Content-Type: application/json; charset=utf-8"})
51 | @POST("/robot/send")
52 | Mono> robotSendMono(@Query("access_token") String accessToken, @Query("timestamp") Long timestamp, @Query("sign") String sign, @Body RequestBody body);
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/notice/dingding/DingTextMessage.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.notice.dingding;
2 |
3 | import cn.hutool.json.JSONObject;
4 | import cn.hutool.json.JSONUtil;
5 | import com.itkevin.common.notice.model.BaseMessage;
6 | import lombok.Data;
7 | import lombok.EqualsAndHashCode;
8 |
9 | import java.io.Serializable;
10 |
11 | @EqualsAndHashCode(callSuper = true)
12 | @Data
13 | public class DingTextMessage extends BaseMessage implements Serializable {
14 | private static final long serialVersionUID = 1L;
15 |
16 | /**
17 | * 消息类型
18 | */
19 | private String msgType = "text";
20 |
21 | /**
22 | * 消息内容
23 | */
24 | private String content;
25 |
26 | @Override
27 | public String toString() {
28 | JSONObject content = new JSONObject();
29 | content.put("content", this.getContent());
30 | JSONObject json = new JSONObject();
31 | json.put("msgtype", this.getMsgType());
32 | json.put("at", this.setAtAllAndMobile(this.getAtMobiles()));
33 | json.put("text", content);
34 | return JSONUtil.toJsonStr(json);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/notice/model/BaseMessage.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.notice.model;
2 |
3 | import cn.hutool.core.collection.CollectionUtil;
4 | import cn.hutool.json.JSONObject;
5 | import com.itkevin.common.enums.LogLevelEnum;
6 | import lombok.Data;
7 |
8 | import java.io.Serializable;
9 | import java.util.List;
10 |
11 | @Data
12 | public class BaseMessage implements Serializable {
13 |
14 | private static final long serialVersionUID = 1L;
15 |
16 | /**
17 | * {@link LogLevelEnum}
18 | * 消息级别
19 | */
20 | private String level;
21 |
22 | /**
23 | * at所有人
24 | */
25 | private Boolean isAtAll;
26 |
27 | /**
28 | * at成员
29 | */
30 | private List atMobiles;
31 |
32 | /**
33 | * at成员
34 | * @param atMobiles 手机号
35 | * @return
36 | */
37 | public JSONObject setAtAllAndMobile(List atMobiles) {
38 | JSONObject atMobile = new JSONObject();
39 | if (!CollectionUtil.isEmpty(atMobiles)) {
40 | atMobile.put("atMobiles", atMobiles);
41 | atMobile.put("isAtAll", false);
42 | }
43 | return atMobile;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/notice/workwx/WeWorkConfigData.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.notice.workwx;
2 |
3 | import lombok.Data;
4 |
5 | import java.io.Serializable;
6 |
7 | @Data
8 | public class WeWorkConfigData implements Serializable {
9 | private static final long serialVersionUID = 1L;
10 |
11 | /**
12 | * webHook
13 | */
14 | private String webHook;
15 |
16 | /**
17 | * key
18 | */
19 | private String key;
20 |
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/notice/workwx/WeWorkMarkDownMessage.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.notice.workwx;
2 |
3 | import cn.hutool.json.JSONObject;
4 | import cn.hutool.json.JSONUtil;
5 | import com.itkevin.common.notice.MarkDownBaseMessage;
6 | import lombok.Data;
7 | import lombok.EqualsAndHashCode;
8 |
9 | import java.io.Serializable;
10 |
11 | @EqualsAndHashCode(callSuper = true)
12 | @Data
13 | public class WeWorkMarkDownMessage extends MarkDownBaseMessage implements Serializable {
14 | private static final long serialVersionUID = 1L;
15 |
16 | /**
17 | * 消息类型
18 | */
19 | private String msgType = "markdown";
20 |
21 | /**
22 | * 消息标题
23 | */
24 | private String title;
25 |
26 | /**
27 | * 消息内容
28 | */
29 | private String content;
30 |
31 | @Override
32 | public String toString() {
33 | JSONObject markdownContent = new JSONObject();
34 | markdownContent.put("title", this.getTitle());
35 | markdownContent.put("content", this.getContent());
36 | markdownContent.put("mentioned_list", this.getAtMobiles());
37 | JSONObject json = new JSONObject();
38 | json.put("msgtype", this.getMsgType());
39 | json.put("markdown", markdownContent);
40 | return JSONUtil.toJsonStr(json);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/notice/workwx/WeWorkTextMessage.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.notice.workwx;
2 |
3 | import cn.hutool.json.JSONObject;
4 | import cn.hutool.json.JSONUtil;
5 | import com.itkevin.common.notice.model.BaseMessage;
6 | import lombok.Data;
7 | import lombok.EqualsAndHashCode;
8 |
9 | import java.io.Serializable;
10 |
11 | @EqualsAndHashCode(callSuper = true)
12 | @Data
13 | public class WeWorkTextMessage extends BaseMessage implements Serializable {
14 | private static final long serialVersionUID = 1L;
15 |
16 | /**
17 | * 消息类型
18 | */
19 | private String msgType = "text";
20 |
21 | /**
22 | * 消息内容
23 | */
24 | private String content;
25 |
26 | @Override
27 | public String toString() {
28 | JSONObject content = new JSONObject();
29 | content.put("content", this.getContent());
30 | JSONObject json = new JSONObject();
31 | json.put("msgtype", this.getMsgType());
32 | json.put("at", this.setAtAllAndMobile(this.getAtMobiles()));
33 | json.put("text", content);
34 | return JSONUtil.toJsonStr(json);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/notice/workwx/WorkWeiXinTalkNotice.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.notice.workwx;
2 |
3 | import cn.hutool.core.util.CharsetUtil;
4 | import cn.hutool.http.HttpUtil;
5 | import cn.hutool.json.JSONObject;
6 | import cn.hutool.json.JSONUtil;
7 | import com.itkevin.common.config.SysConfig;
8 | import com.itkevin.common.enums.LogLevelEnum;
9 | import com.itkevin.common.notice.MarkDownBaseMessage;
10 | import com.itkevin.common.notice.NoticeInterface;
11 | import com.itkevin.common.util.LocalCacheUtils;
12 | import com.itkevin.common.util.StringConverterFactory;
13 | import com.jakewharton.retrofit2.adapter.reactor.ReactorCallAdapterFactory;
14 | import lombok.extern.slf4j.Slf4j;
15 | import okhttp3.MediaType;
16 | import okhttp3.RequestBody;
17 | import okhttp3.ResponseBody;
18 | import org.apache.commons.lang3.StringUtils;
19 | import reactor.core.publisher.Mono;
20 | import retrofit2.Response;
21 | import retrofit2.Retrofit;
22 | import retrofit2.converter.gson.GsonConverterFactory;
23 |
24 | import java.util.List;
25 | import java.util.Map;
26 |
27 | @Slf4j
28 | public class WorkWeiXinTalkNotice implements NoticeInterface {
29 | /**
30 | * 机器人标识
31 | */
32 | private static final String WEBHOOK_INDEX = "WEWORK_WEBHOOK_INDEX";
33 |
34 | /**
35 | * baseUrl
36 | */
37 | private static final String BASE_URL = "https://qyapi.weixin.qq.com";
38 |
39 | private static volatile WorkWeiXinTalkNotice workWeiXinTalkNotice;
40 |
41 | public static WorkWeiXinTalkNotice getInstance(){
42 | try {
43 | if(null == workWeiXinTalkNotice){
44 | synchronized (WorkWeiXinTalkNotice.class){
45 | if(null == workWeiXinTalkNotice){
46 | workWeiXinTalkNotice = new WorkWeiXinTalkNotice();
47 | }
48 | }
49 | }
50 | }catch (Exception e){
51 | e.printStackTrace();
52 | }
53 | return workWeiXinTalkNotice;
54 | }
55 |
56 | /**
57 | * 服务接口(retrofit2-reactor-adapter:https://github.com/JakeWharton/retrofit2-reactor-adapter)
58 | */
59 | private static WorkWeiXinTalkService workWeiXinTalkService = new Retrofit.Builder()
60 | .baseUrl(BASE_URL)
61 | .client(NoticeInterface.initOkHttpClient())
62 | .addConverterFactory(new StringConverterFactory())
63 | .addConverterFactory(GsonConverterFactory.create())
64 | .addCallAdapterFactory(ReactorCallAdapterFactory.create())
65 | .build()
66 | .create(WorkWeiXinTalkService.class);
67 |
68 | /**
69 | * 发送消息
70 | * @param markDownBaseMessage
71 | */
72 | @Override
73 | public void sendMessage(MarkDownBaseMessage markDownBaseMessage) {
74 | try {
75 | String level = markDownBaseMessage.getLevel();
76 | String alarmDingTalk = StringUtils.isNotBlank(level) && level.equals(LogLevelEnum.SERIOUS.name())
77 | ? SysConfig.instance.getAlarmSeriousTalkHook()
78 | : SysConfig.instance.getAlarmTalkHook();
79 | if (StringUtils.isBlank(alarmDingTalk)) {
80 | log.warn("log skyeye >>> config 'skyeye.log.alarm.weWorktalk' or 'skyeye.log.alarm.serious.weWorktalk' is null");
81 | return;
82 | }
83 | List weWorkConfigDataList = JSONUtil.toList(JSONUtil.parseArray(alarmDingTalk), WeWorkConfigData.class);
84 | String str = converMarkDownBaseMessage2Str(markDownBaseMessage);
85 | send(weWorkConfigDataList, str);
86 | } catch (Exception e) {
87 | log.warn("log skyeye >>> WeWorkTalkUtils.sendMessage occur exception", e);
88 | }
89 | }
90 |
91 | private String converMarkDownBaseMessage2Str(MarkDownBaseMessage markDownBaseMessage) {
92 | JSONObject markdownContent = new JSONObject();
93 | markdownContent.put("title", markDownBaseMessage.getTitle());
94 | markdownContent.put("content", markDownBaseMessage.getContent());
95 | markdownContent.put("mentioned_list", markDownBaseMessage.getAtMobiles());
96 | JSONObject json = new JSONObject();
97 | json.put("msgtype", markDownBaseMessage.getMsgType());
98 | json.put("markdown", markdownContent);
99 | return JSONUtil.toJsonStr(json);
100 | }
101 |
102 |
103 | /**
104 | * 发送
105 | * @param weWorkConfigDataList
106 | * @param jsonContent
107 | */
108 | private void send(List weWorkConfigDataList, String jsonContent) {
109 | int currentIndex = getIndex(weWorkConfigDataList.size());
110 | WeWorkConfigData weWorkConfigData = weWorkConfigDataList.get(currentIndex);
111 | String key = weWorkConfigData.getKey();
112 | if (StringUtils.isBlank(key)) {
113 | Map paramMap = HttpUtil.decodeParamMap(weWorkConfigData.getWebHook(), CharsetUtil.defaultCharset());
114 | key = paramMap.get("key");
115 | }
116 | send(key, jsonContent);
117 | }
118 |
119 | /**
120 | * 发送
121 | * @param key
122 | * @param jsonContent
123 | */
124 | private boolean send(String key, String jsonContent) {
125 | try {
126 | RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), jsonContent);
127 | log.info("log skyeye >>> send weWorktalk key: {}", key);
128 | Mono> mono = workWeiXinTalkService.robotSendMono(key, requestBody);
129 | ResponseBody responseBody = mono.blockOptional().map(Response::body).orElse(null);
130 | String string = responseBody != null ? responseBody.string() : "";
131 | log.info("log skyeye >>> send weWorktalk result: {}", string);
132 | if (string.contains("")) {
133 | return false;
134 | }
135 | JSONObject result = JSONUtil.parseObj(string);
136 | if (result.get("errcode") == null || !"0".equals(String.valueOf(result.get("errcode")))) {
137 | return false;
138 | }
139 | return true;
140 | } catch (Exception e) {
141 | log.warn("log skyeye >>> weWorkTalkUtils.send occur exception", e);
142 | return false;
143 | }
144 | }
145 |
146 | /**
147 | * 获取第几个机器人
148 | * @param totalCount
149 | * @return
150 | */
151 | private synchronized int getIndex(int totalCount) {
152 | int currentIndex = LocalCacheUtils.getIntCache(WEBHOOK_INDEX);
153 | if (currentIndex > totalCount - 1) {
154 | currentIndex = 0;
155 | LocalCacheUtils.putIntCache(WEBHOOK_INDEX, currentIndex);
156 | }
157 | LocalCacheUtils.incr(WEBHOOK_INDEX);
158 | return currentIndex;
159 | }
160 |
161 | @Override
162 | public String filterFlag() {
163 | return "wework";
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/notice/workwx/WorkWeiXinTalkService.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.notice.workwx;
2 |
3 | import com.itkevin.common.notice.NoticeInterface;
4 | import okhttp3.RequestBody;
5 | import okhttp3.ResponseBody;
6 | import reactor.core.publisher.Mono;
7 | import retrofit2.Call;
8 | import retrofit2.Response;
9 | import retrofit2.http.Body;
10 | import retrofit2.http.Headers;
11 | import retrofit2.http.POST;
12 | import retrofit2.http.Query;
13 |
14 | /**
15 | * 企业微信服务接口
16 | */
17 | public interface WorkWeiXinTalkService {
18 |
19 |
20 | /**
21 | * 发送企业微信消息
22 | *
23 | * @param key
24 | * @param body
25 | * @return
26 | */
27 | @Headers({"Content-Type: application/json; charset=utf-8"})
28 | @POST("/cgi-bin/webhook/send")
29 | Call robotSendCall(@Query("key") String key, @Body RequestBody body);
30 |
31 | /**
32 | * 发送企业微信消息
33 | *
34 | * @param key
35 | * @param body
36 | * @return
37 | */
38 | @Headers({"Content-Type: application/json; charset=utf-8"})
39 | @POST("/cgi-bin/webhook/send")
40 | Mono> robotSendMonoStr(@Query("key") String key, @Body RequestBody body);
41 |
42 | /**
43 | * 发送企业微信消息
44 | *
45 | * @param key
46 | * @param body
47 | * @return
48 | */
49 | @Headers({"Content-Type: application/json; charset=utf-8"})
50 | @POST("/cgi-bin/webhook/send")
51 | Mono> robotSendMono(@Query("key") String key, @Body RequestBody body);
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/BatchConsumerCallbackAdvisor.java:
--------------------------------------------------------------------------------
1 | //package com.itkevin.common.util;
2 | //
3 | //import com.ctrip.framework.foundation.Foundation;
4 | //import com.google.common.collect.Lists;
5 | //import com.itkevin.common.enums.MDCConstantEnum;
6 | //import com.itkevin.common.enums.RequestTypeEnum;
7 | //import lombok.extern.slf4j.Slf4j;
8 | //import net.bytebuddy.asm.Advice;
9 | //import org.apache.rocketmq.common.message.MessageExt;
10 | //import org.apache.skywalking.apm.toolkit.trace.TraceContext;
11 | //
12 | //import java.lang.reflect.Method;
13 | //import java.util.List;
14 | //import java.util.stream.Collectors;
15 | //
16 | //@Slf4j
17 | //public class BatchConsumerCallbackAdvisor {
18 | //
19 | // @Advice.OnMethodEnter
20 | // public static void onMethodEnter(@Advice.This Object object, @Advice.Origin Method method, @Advice.AllArguments Object[] arguments) {
21 | // try {
22 | // @SuppressWarnings("unchecked")
23 | // List batchMessage = arguments != null && arguments.length > 0 ? (List) arguments[0] : null;
24 | // if (!CollectionUtil.isEmpty(batchMessage)) {
25 | // List topicList = Lists.newArrayList();
26 | // List msgIdList = Lists.newArrayList();
27 | // List keysList = Lists.newArrayList();
28 | // for (BatchConsumerCallback.MQMessage mqMessage : batchMessage) {
29 | // MessageExt messageExt = (MessageExt) mqMessage.getMessageExt();
30 | // topicList.add(messageExt.getTopic());
31 | // msgIdList.add(messageExt.getMsgId());
32 | // keysList.add(messageExt.getKeys());
33 | // }
34 | // MDCUtils.put(MDCConstantEnum.SERVER_NAME.getCode(), Foundation.app().getAppId());
35 | // MDCUtils.put(MDCConstantEnum.SERVER_HOSTNAME.getCode(), IPUtils.getLocalHostName());
36 | // MDCUtils.put(MDCConstantEnum.SERVER_IP.getCode(), IPUtils.getLocalIp());
37 | // MDCUtils.put(MDCConstantEnum.REQUEST_TYPE.getCode(), RequestTypeEnum.MQ.name().toLowerCase());
38 | // MDCUtils.put(MDCConstantEnum.TRACE_ID.getCode(), TraceContext.traceId());
39 | // MDCUtils.put(MDCConstantEnum.REQUEST_URI.getCode(), object.getClass().getName() + "#call");
40 | // MDCUtils.put(MDCConstantEnum.MESSAGE_TOPIC.getCode(), topicList.stream().distinct().collect(Collectors.joining(",")));
41 | // MDCUtils.put(MDCConstantEnum.MESSAGE_ID.getCode(), String.join(",", msgIdList));
42 | // MDCUtils.put(MDCConstantEnum.MESSAGE_KEYS.getCode(), String.join(",", keysList));
43 | // }
44 | // } catch (Exception e) {
45 | // log.warn("log skyeye >>> BatchConsumerCallbackAdvisor.onMethodEnter occur exception", e);
46 | // }
47 | // }
48 | //
49 | // @Advice.OnMethodExit(onThrowable = Throwable.class)
50 | // public static void onMethodExit() {
51 | // MDCUtils.clear();
52 | // }
53 | //
54 | //}
55 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/ByteBuddyPlugin.java:
--------------------------------------------------------------------------------
1 | //package com.itkevin.common.util;
2 | //
3 | //import com.xxl.job.core.handler.IJobHandler;
4 | //import net.bytebuddy.asm.Advice;
5 | //import net.bytebuddy.build.Plugin;
6 | //import net.bytebuddy.description.annotation.AnnotationList;
7 | //import net.bytebuddy.description.type.TypeDescription;
8 | //import net.bytebuddy.dynamic.ClassFileLocator;
9 | //import net.bytebuddy.dynamic.DynamicType;
10 | //import net.bytebuddy.matcher.ElementMatchers;
11 | //
12 | //import java.io.IOException;
13 | //
14 | //public class ByteBuddyPlugin implements Plugin {
15 | //
16 | // @Override
17 | // public DynamicType.Builder> apply(DynamicType.Builder> builder, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
18 | // if (typeDescription.isAssignableTo(ConsumerCallback.class)) {
19 | // return builder.visit(Advice.to(ConsumerCallbackAdvisor.class).on(ElementMatchers.isOverriddenFrom(ConsumerCallback.class).and(ElementMatchers.named("call"))));
20 | // } else if (typeDescription.isAssignableTo(BatchConsumerCallback.class)) {
21 | // return builder.visit(Advice.to(BatchConsumerCallbackAdvisor.class).on(ElementMatchers.isOverriddenFrom(BatchConsumerCallback.class).and(ElementMatchers.named("call"))));
22 | // } else if (typeDescription.isAssignableTo(IJobHandler.class)) {
23 | // return builder.visit(Advice.to(JobHandlerAdvisor.class).on(ElementMatchers.isOverriddenFrom(IJobHandler.class).and(ElementMatchers.named("execute"))));
24 | // } else {
25 | // return builder.visit(Advice.to(EventProcessorAdvisor.class).on(ElementMatchers.isAnnotatedWith(EventProcessor.class)));
26 | // }
27 | // }
28 | //
29 | // @Override
30 | // public void close() throws IOException {
31 | //
32 | // }
33 | //
34 | // @Override
35 | // public boolean matches(TypeDescription typeDefinitions) {
36 | // boolean isConsumerCallback = typeDefinitions.isAssignableTo(ConsumerCallback.class);
37 | // boolean isBatchConsumerCallback = typeDefinitions.isAssignableTo(BatchConsumerCallback.class);
38 | // boolean isJobHander = typeDefinitions.isAssignableTo(IJobHandler.class);
39 | // AnnotationList annotationList = typeDefinitions.getDeclaredAnnotations();
40 | // return isConsumerCallback || isBatchConsumerCallback || isJobHander || (!CollectionUtil.isEmpty(annotationList) && annotationList.isAnnotationPresent(EventProcessor.class));
41 | // }
42 | //}
43 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/ByteBuddyUtils.java:
--------------------------------------------------------------------------------
1 | //package com.itkevin.common.util;
2 | //
3 | //import com.xxl.job.core.handler.IJobHandler;
4 | //import com.xxl.job.core.handler.annotation.JobHandler;
5 | //import lombok.extern.slf4j.Slf4j;
6 | //import net.bytebuddy.ByteBuddy;
7 | //import net.bytebuddy.agent.ByteBuddyAgent;
8 | //import net.bytebuddy.asm.Advice;
9 | //import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
10 | //import net.bytebuddy.matcher.ElementMatchers;
11 | //import org.springframework.aop.support.AopUtils;
12 | //import org.springframework.context.ApplicationContext;
13 | //
14 | //import java.util.Map;
15 | //
16 | //@Slf4j
17 | //public class ByteBuddyUtils {
18 | //
19 | // /**
20 | // * 字节码增强
21 | // * @param applicationContext
22 | // */
23 | // public static void byteBuddy(ApplicationContext applicationContext) {
24 | // // RocketMq消息者回调实现类增强
25 | // byteBuddyMq(applicationContext);
26 | // // job任务类增强
27 | // byteBuddyJob(applicationContext);
28 | // }
29 | //
30 | // /**
31 | // * RocketMq消息者回调实现类增强
32 | // * @param applicationContext
33 | // */
34 | // private static void byteBuddyMq(ApplicationContext applicationContext) {
35 | // Map consumerCallbackMap = applicationContext.getBeansOfType(ConsumerCallback.class);
36 | // if (!CollectionUtil.isEmpty(consumerCallbackMap)) {
37 | // consumerCallbackMap.forEach((name, consumerCallback) -> {
38 | // try{
39 | // log.info("byteBuddyMq consumerCallback : {}, class {}",name,AopUtils.getTargetClass(consumerCallback));
40 | // ByteBuddyAgent.install();
41 | // new ByteBuddy().redefine(AopUtils.getTargetClass(consumerCallback))
42 | // .visit(Advice.to(ConsumerCallbackAdvisor.class).on(ElementMatchers.isOverriddenFrom(ConsumerCallback.class).and(ElementMatchers.named("call"))))
43 | // .make()
44 | // .load(consumerCallback.getClass().getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
45 | // }catch (Exception e){
46 | // log.warn("byteBuddyMq error, e: {}",e.getMessage());
47 | // }
48 | //
49 | // });
50 | // }
51 | // Map batchConsumerCallbackMap = applicationContext.getBeansOfType(BatchConsumerCallback.class);
52 | // if (!CollectionUtil.isEmpty(batchConsumerCallbackMap)) {
53 | // batchConsumerCallbackMap.forEach((name, batchConsumerCallback) -> {
54 | // try{
55 | // log.info("byteBuddyMq batchConsumerCallback : {}, class {}: ",name,AopUtils.getTargetClass(batchConsumerCallback));
56 | // ByteBuddyAgent.install();
57 | // new ByteBuddy().redefine(AopUtils.getTargetClass(batchConsumerCallback))
58 | // .visit(Advice.to(BatchConsumerCallbackAdvisor.class).on(ElementMatchers.isOverriddenFrom(BatchConsumerCallback.class).and(ElementMatchers.named("call"))))
59 | // .make()
60 | // .load(batchConsumerCallback.getClass().getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
61 | // }catch (Exception e){
62 | // log.warn("byteBuddyMq error, e: {}",e.getMessage());
63 | // }
64 | // });
65 | // }
66 | // }
67 | //
68 | // /**
69 | // * job任务类增强
70 | // * @param applicationContext
71 | // */
72 | // private static void byteBuddyJob(ApplicationContext applicationContext) {
73 | // Map jobHandlerMap = applicationContext.getBeansWithAnnotation(JobHandler.class);
74 | // if (!CollectionUtil.isEmpty(jobHandlerMap)) {
75 | // try{
76 | // jobHandlerMap.forEach((name, jobHandler) -> {
77 | // log.info("byteBuddyJob : {},class : {}",name,AopUtils.getTargetClass(jobHandler));
78 | // ByteBuddyAgent.install();
79 | // new ByteBuddy().redefine(AopUtils.getTargetClass(jobHandler))
80 | // .visit(Advice.to(JobHandlerAdvisor.class).on(ElementMatchers.isOverriddenFrom(IJobHandler.class).and(ElementMatchers.named("execute"))))
81 | // .make()
82 | // .load(jobHandler.getClass().getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
83 | // });
84 | // }catch (Exception e){
85 | // log.warn("byteBuddyJob error, e: {}",e.getMessage());
86 | // }
87 | //
88 | // }
89 | // }
90 | //}
91 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/CommonConverter.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import ma.glasnost.orika.MapperFacade;
4 | import ma.glasnost.orika.MapperFactory;
5 | import ma.glasnost.orika.impl.DefaultMapperFactory;
6 |
7 | public class CommonConverter {
8 | private static MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
9 | private static MapperFacade mapperFacade = null;
10 |
11 | static {
12 | CommonConverter.mapperFacade = CommonConverter.mapperFactory.getMapperFacade();
13 | }
14 |
15 | private CommonConverter() {
16 |
17 | }
18 |
19 | public static MapperFacade getConverter() {
20 | return CommonConverter.mapperFacade;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/ConfigUtils.java:
--------------------------------------------------------------------------------
1 | //package com.itkevin.common.util;
2 | //
3 | //import cn.hutool.core.util.NumberUtil;
4 | //import com.ctrip.framework.apollo.Config;
5 | //import com.ctrip.framework.apollo.ConfigService;
6 | //import lombok.extern.slf4j.Slf4j;
7 | //import org.apache.commons.lang3.StringUtils;
8 | //
9 | //import java.util.Properties;
10 | //
11 | //@Slf4j
12 | //public class ConfigUtils {
13 | //
14 | // /**
15 | // * 配置属性
16 | // */
17 | // private static final Properties properties = new Properties();
18 | //
19 | // /**
20 | // * config instance
21 | // * @return
22 | // */
23 | // public static Config getConfig() {
24 | // return ConfigService.getConfig("skyeye");
25 | // }
26 | //
27 | // /**
28 | // * 获取String类型配置
29 | // * @param key
30 | // * @param defaultValue
31 | // * @return
32 | // */
33 | // public static String getProperty(String key, String defaultValue) {
34 | // if (StringUtils.isBlank(key)) {
35 | // return defaultValue;
36 | // }
37 | // try {
38 | // return properties.getProperty(key, defaultValue);
39 | // } catch (Exception e) {
40 | // return defaultValue;
41 | // }
42 | // }
43 | //
44 | // /**
45 | // * 获取Integer类型配置
46 | // * @param key
47 | // * @param defaultValue
48 | // * @return
49 | // */
50 | // public static Integer getIntProperty(String key, Integer defaultValue) {
51 | // if (StringUtils.isBlank(key)) {
52 | // return defaultValue;
53 | // }
54 | // try {
55 | // Object o = properties.getOrDefault(key, defaultValue);
56 | // return NumberUtil.isInteger(o.toString()) ? Integer.parseInt(o.toString()) : defaultValue;
57 | // } catch (Exception e) {
58 | // return defaultValue;
59 | // }
60 | // }
61 | //
62 | // /**
63 | // * 获取Long类型配置
64 | // * @param key
65 | // * @param defaultValue
66 | // * @return
67 | // */
68 | // public static Long getLongProperty(String key, Long defaultValue) {
69 | // if (StringUtils.isBlank(key)) {
70 | // return defaultValue;
71 | // }
72 | // try {
73 | // Object o = properties.getOrDefault(key, defaultValue);
74 | // return NumberUtil.isLong(o.toString()) ? Long.parseLong(o.toString()) : defaultValue;
75 | // } catch (Exception e) {
76 | // return defaultValue;
77 | // }
78 | // }
79 | //
80 | // /**
81 | // * 存储配置
82 | // * @param key
83 | // * @param value
84 | // */
85 | // public static void saveProperty(String key, String value) {
86 | // if (StringUtils.isBlank(key) || value == null) {
87 | // return;
88 | // }
89 | // try {
90 | // properties.put(key, value);
91 | // } catch (Exception e) {
92 | // e.printStackTrace();
93 | // }
94 | // }
95 | //
96 | // /**
97 | // * 删除配置
98 | // * @param key
99 | // */
100 | // public static void removeProperty(String key) {
101 | // if (StringUtils.isBlank(key)) {
102 | // return;
103 | // }
104 | // try {
105 | // properties.remove(key);
106 | // } catch (Exception e) {
107 | // e.printStackTrace();
108 | // }
109 | // }
110 | //
111 | //}
112 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/ConsumerCallbackAdvisor.java:
--------------------------------------------------------------------------------
1 | //package com.itkevin.common.util;
2 | //
3 | //import com.ctrip.framework.foundation.Foundation;
4 | //import com.skyeye.k12.teacher.common.enums.MDCConstantEnum;
5 | //import com.skyeye.k12.teacher.common.enums.RequestTypeEnum;
6 | //import lombok.extern.slf4j.Slf4j;
7 | //import net.bytebuddy.asm.Advice;
8 | //import org.apache.rocketmq.common.message.MessageExt;
9 | //import org.apache.skywalking.apm.toolkit.trace.TraceContext;
10 | //
11 | //import java.lang.reflect.Method;
12 | //
13 | //@Slf4j
14 | //public class ConsumerCallbackAdvisor {
15 | //
16 | // @Advice.OnMethodEnter
17 | // public static void onMethodEnter(@Advice.This Object object, @Advice.Origin Method method, @Advice.AllArguments Object[] arguments) {
18 | // try {
19 | // MessageExt messageExt = arguments != null && arguments.length > 0 ? (MessageExt) arguments[1] : null;
20 | // MDCUtils.put(MDCConstantEnum.SERVER_NAME.getCode(), Foundation.app().getAppId());
21 | // MDCUtils.put(MDCConstantEnum.SERVER_IP.getCode(), IPUtils.getLocalIp());
22 | // MDCUtils.put(MDCConstantEnum.SERVER_HOSTNAME.getCode(), IPUtils.getLocalHostName());
23 | // MDCUtils.put(MDCConstantEnum.REQUEST_TYPE.getCode(), RequestTypeEnum.MQ.name().toLowerCase());
24 | // MDCUtils.put(MDCConstantEnum.TRACE_ID.getCode(), TraceContext.traceId());
25 | // MDCUtils.put(MDCConstantEnum.REQUEST_URI.getCode(), object.getClass().getName() + "#call");
26 | // MDCUtils.put(MDCConstantEnum.MESSAGE_TOPIC.getCode(), messageExt != null ? messageExt.getTopic() : "");
27 | // MDCUtils.put(MDCConstantEnum.MESSAGE_ID.getCode(), messageExt != null ? messageExt.getMsgId() : "");
28 | // MDCUtils.put(MDCConstantEnum.MESSAGE_KEYS.getCode(), messageExt != null ? messageExt.getKeys() : "");
29 | // } catch (Exception e) {
30 | // log.warn("log skyeye >>> ConsumerCallbackAdvisor.onMethodEnter occur exception", e);
31 | // }
32 | // }
33 | //
34 | // @Advice.OnMethodExit(onThrowable = Throwable.class)
35 | // public static void onMethodExit() {
36 | // MDCUtils.clear();
37 | // }
38 | //
39 | //}
40 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/ElapsedUtils.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import cn.hutool.core.collection.CollectionUtil;
4 | import cn.hutool.json.JSONUtil;
5 | import com.google.common.collect.Lists;
6 | import com.itkevin.common.config.SysConfig;
7 | import com.itkevin.common.constants.SysConstant;
8 | import com.itkevin.common.enums.BusinessTypeEnum;
9 | import com.itkevin.common.enums.MDCConstantEnum;
10 | import com.itkevin.common.model.BusinessData;
11 | import com.itkevin.common.model.HashedWheelData;
12 | import com.itkevin.common.model.UriElapsedCollect;
13 | import com.itkevin.common.model.UriElapsedData;
14 | import lombok.extern.slf4j.Slf4j;
15 | import org.apache.commons.lang3.StringUtils;
16 | import reactor.core.publisher.Mono;
17 | import reactor.core.scheduler.Schedulers;
18 |
19 | import java.util.List;
20 | import java.util.concurrent.ExecutorService;
21 | import java.util.concurrent.LinkedBlockingQueue;
22 | import java.util.concurrent.ThreadPoolExecutor;
23 | import java.util.concurrent.TimeUnit;
24 | import java.util.stream.Collectors;
25 |
26 | @Slf4j
27 | public class ElapsedUtils {
28 |
29 | /**
30 | * 接口耗时统一前缀
31 | */
32 | public static final String URI_ELAPSED = "URI_ELAPSED_";
33 |
34 | /**
35 | * 接口耗时traceId集合统一后缀
36 | */
37 | public static final String URI_ELAPSED_TRACEID_LIST = "_URI_ELAPSED_TRACEID_LIST";
38 |
39 | /**
40 | * 接口最大耗时统一后缀
41 | */
42 | public static final String MAX_URI_ELAPSED = "_MAX_URI_ELAPSED";
43 |
44 | /**
45 | * 接口最大耗时traceId统一后缀
46 | */
47 | public static final String MAX_URI_ELAPSED_TRACEID = "_MAX_URI_ELAPSED_TRACEID";
48 |
49 | /**
50 | * 接口uri耗时聚合-指定线程池
51 | */
52 | private static ExecutorService executorService = new ThreadPoolExecutor(SysConstant.THREAD_NUM, SysConstant.MAX_THREAD_NUM, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1), new ThreadPoolExecutor.DiscardPolicy());
53 |
54 | /**
55 | * 接口uri耗时聚合
56 | * @param uriElapsedCollect
57 | */
58 | public static void uriElapsed(UriElapsedCollect uriElapsedCollect) {
59 | Mono.fromRunnable(() -> {
60 | try {
61 | long elapsed = uriElapsedCollect.getElapsed();
62 | String requestURI = uriElapsedCollect.getRequestURI();
63 | if (StringUtils.isNotBlank(requestURI)) {
64 | requestURI = requestURI.replaceAll("/\\d+", "/{PathVariable}");
65 | // 接口耗时报警间隔时间、接口耗时超过阀值时间次数
66 | Integer alarmUriElapsedTime = SysConfig.instance.getAlarmUriElapsedTime();
67 | Integer alarmUriElapsedCount = SysConfig.instance.getAlarmUriElapsedCount();
68 | if (alarmUriElapsedTime == null || alarmUriElapsedCount == null || alarmUriElapsedTime == 0 || alarmUriElapsedCount == 0) {
69 | return;
70 | }
71 | // 获取指定URI接口耗时时间阀值
72 | long elapsedThreshold = getUriElapsedThreshold(requestURI);
73 | // 耗时超过阀值
74 | if (elapsed > elapsedThreshold) {
75 | // 耗时超过阀值的次数
76 | String uniqueKey = URI_ELAPSED + requestURI;
77 | // 存储缓存数据
78 | storageData(uniqueKey, uriElapsedCollect);
79 | // 计算次数
80 | Integer count = LocalCacheUtils.incr(uniqueKey);
81 | if (count.compareTo(1) == 0) {
82 | // 第一次耗时超过阀值时则给时间轮上添加任务
83 | BusinessData businessData = new BusinessData();
84 | businessData.setRequestURI(requestURI);
85 | businessData.setServerName(uriElapsedCollect.getServerName());
86 | businessData.setServerIP(uriElapsedCollect.getServerIP());
87 | businessData.setServerHostname(uriElapsedCollect.getServerHostname());
88 | HashedWheelUtils.putWheelQueue(new HashedWheelData(alarmUriElapsedTime, BusinessTypeEnum.URI_ELAPSED.name(), uniqueKey, JSONUtil.toJsonStr(businessData)));
89 | }
90 | }
91 | }
92 | } catch (Throwable e) {
93 | log.warn("log skyeye >>> ElapsedUtils.uriElapsed occur exception", e);
94 | }
95 | }).subscribeOn(Schedulers.fromExecutorService(executorService, "skyeye-uriElapsed-executor")).subscribe();
96 | }
97 |
98 | /**
99 | * 获取指定URI接口耗时时间阀值
100 | * @param requestURI
101 | * @return
102 | */
103 | public static long getUriElapsedThreshold(String requestURI) {
104 | if (StringUtils.isBlank(requestURI)) {
105 | return 0;
106 | }
107 | // 获取指定URI耗时时间阀值
108 | String alarmUriElapsed = SysConfig.instance.getAlarmUriElapsed();
109 | List uriElapsedDataList = StringUtils.isNotBlank(alarmUriElapsed) ? JSONUtil.toList(JSONUtil.parseArray(alarmUriElapsed), UriElapsedData.class) : Lists.newArrayList();
110 | uriElapsedDataList = uriElapsedDataList.stream()
111 | .filter(uriElapsedData -> StringUtils.isNotBlank(uriElapsedData.getUri()))
112 | .peek(uriElapsedData -> uriElapsedData.setUri(uriElapsedData.getUri().replaceAll("/\\d+", "/{PathVariable}")))
113 | .collect(Collectors.toList());
114 | List elapsedList = uriElapsedDataList.stream().filter(uriElapsedData -> uriElapsedData.getUri().equalsIgnoreCase(requestURI)).map(UriElapsedData::getElapsed).collect(Collectors.toList());
115 | // 获取全局接口耗时时间阀值
116 | Long alarmUriElapsedGlobal = SysConfig.instance.getAlarmUriElapsedGlobal();
117 |
118 | return CollectionUtil.isEmpty(elapsedList) ? alarmUriElapsedGlobal : elapsedList.get(0);
119 | }
120 |
121 | /**
122 | * 存储缓存数据
123 | * @param uriElapsedCollect
124 | */
125 | private static void storageData(String uniqueKey, UriElapsedCollect uriElapsedCollect) {
126 | storageTraceidListData(uniqueKey, uriElapsedCollect.getTraceId());
127 | storageMaxElapsedData(uniqueKey, uriElapsedCollect.getElapsed(), uriElapsedCollect.getTraceId());
128 | }
129 |
130 | /**
131 | * 存储traceIdList
132 | * @param uniqueKey
133 | * @param traceId
134 | */
135 | private static synchronized void storageTraceidListData(String uniqueKey, String traceId) {
136 | String traceIdListKey = uniqueKey + URI_ELAPSED_TRACEID_LIST;
137 | List traceIds = LocalCacheUtils.smember(traceIdListKey);
138 | if (CollectionUtil.isEmpty(traceIds)) {
139 | LocalCacheUtils.sadd(traceIdListKey, traceId);
140 | } else {
141 | if (traceIds.size() < 3) {
142 | LocalCacheUtils.sadd(traceIdListKey, traceId);
143 | }
144 | }
145 | }
146 |
147 | /**
148 | * 存储最大耗时和最大耗时traceId
149 | * @param uniqueKey
150 | * @param elapsed
151 | * @param traceId
152 | */
153 | private static synchronized void storageMaxElapsedData(String uniqueKey, long elapsed, String traceId) {
154 | String maxElapsedKey = uniqueKey + MAX_URI_ELAPSED;
155 | String maxElapsedTraceIdKey = uniqueKey + MAX_URI_ELAPSED + MAX_URI_ELAPSED_TRACEID;
156 | Long maxElapsed = LocalCacheUtils.getLongCache(maxElapsedKey);
157 | if (elapsed > maxElapsed) {
158 | LocalCacheUtils.putLongCache(maxElapsedKey, elapsed);
159 | LocalCacheUtils.putCache(maxElapsedTraceIdKey, traceId);
160 | }
161 | }
162 |
163 | /**
164 | * URI数据采集
165 | * @param elapsed
166 | * @return
167 | */
168 | public static UriElapsedCollect uriElapsedCollect(long elapsed) {
169 | UriElapsedCollect uriElapsedCollect = new UriElapsedCollect();
170 | uriElapsedCollect.setRequestURI(MDCUtils.get(MDCConstantEnum.REQUEST_URI.getCode()));
171 | uriElapsedCollect.setElapsed(elapsed);
172 | uriElapsedCollect.setTraceId(MDCUtils.get(MDCConstantEnum.TRACE_ID.getCode()));
173 | uriElapsedCollect.setServerName(MDCUtils.get(MDCConstantEnum.SERVER_NAME.getCode()));
174 | uriElapsedCollect.setServerIP(MDCUtils.get(MDCConstantEnum.SERVER_IP.getCode()));
175 | uriElapsedCollect.setServerHostname(MDCUtils.get(MDCConstantEnum.SERVER_HOSTNAME.getCode()));
176 |
177 | return uriElapsedCollect;
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/EventProcessorAdvisor.java:
--------------------------------------------------------------------------------
1 | //package com.itkevin.common.util;
2 | //
3 | //import com.ctrip.framework.foundation.Foundation;
4 | //import com.itkevin.common.enums.MDCConstantEnum;
5 | //import com.itkevin.common.enums.RequestTypeEnum;
6 | //import lombok.extern.slf4j.Slf4j;
7 | //import net.bytebuddy.asm.Advice;
8 | //import org.apache.skywalking.apm.toolkit.trace.TraceContext;
9 | //import sun.plugin2.message.EventMessage;
10 | //
11 | //import java.lang.reflect.Method;
12 | //
13 | //@Slf4j
14 | //public class EventProcessorAdvisor {
15 | //
16 | // @Advice.OnMethodEnter
17 | // public static void onMethodEnter(@Advice.This Object object, @Advice.Origin Method method, @Advice.AllArguments Object[] arguments) {
18 | // try {
19 | // Object argument = arguments != null && arguments.length > 0 ? arguments[0] : null;
20 | // EventMessage eventMessage = argument instanceof EventMessage ? (EventMessage) argument : null;
21 | // MDCUtils.put(MDCConstantEnum.SERVER_NAME.getCode(), Foundation.app().getAppId());
22 | // MDCUtils.put(MDCConstantEnum.SERVER_IP.getCode(), IPUtils.getLocalIp());
23 | // MDCUtils.put(MDCConstantEnum.SERVER_HOSTNAME.getCode(), IPUtils.getLocalHostName());
24 | // MDCUtils.put(MDCConstantEnum.REQUEST_TYPE.getCode(), RequestTypeEnum.EVENT.name().toLowerCase());
25 | // MDCUtils.put(MDCConstantEnum.TRACE_ID.getCode(), TraceContext.traceId());
26 | // MDCUtils.put(MDCConstantEnum.REQUEST_URI.getCode(), object.getClass().getName() + "#" + method.getName());
27 | // MDCUtils.put(MDCConstantEnum.EVENT_NAME.getCode(), eventMessage != null ? eventMessage.getEventName() : "");
28 | // MDCUtils.put(MDCConstantEnum.EVENT_PAYLOAD.getCode(), eventMessage != null ? eventMessage.getPayload() : "");
29 | // } catch (Exception e) {
30 | // log.warn("log skyeye >>> EventProcessorAdvisor.onMethodEnter occur exception", e);
31 | // }
32 | // }
33 | //
34 | // @Advice.OnMethodExit(onThrowable = Throwable.class)
35 | // public static void onMethodExit() {
36 | // MDCUtils.clear();
37 | // }
38 | //
39 | //}
40 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/GuavaCacheUtils.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import cn.hutool.core.collection.CollectionUtil;
4 | import com.google.common.cache.Cache;
5 | import com.google.common.cache.CacheBuilder;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.apache.commons.lang3.StringUtils;
8 |
9 | import java.util.Calendar;
10 | import java.util.GregorianCalendar;
11 | import java.util.List;
12 | import java.util.concurrent.Callable;
13 | import java.util.concurrent.TimeUnit;
14 |
15 | @Slf4j
16 | public class GuavaCacheUtils {
17 |
18 | /**
19 | * 缓存操作对象
20 | */
21 | private static Cache cache;
22 |
23 | static {
24 | cache = CacheBuilder.newBuilder()
25 | .maximumSize(1000)
26 | .expireAfterWrite(1, TimeUnit.HOURS)
27 | //.removalListener(notification -> log.info("log skyeye >>> {} was removed from guava cache, cause is {}", notification.getKey(), notification.getCause()))
28 | .build();
29 | }
30 |
31 | /**
32 | * 当天剩余时间
33 | * @return
34 | */
35 | private static long getMilliSeconds() {
36 | Calendar curDate = Calendar.getInstance();
37 | Calendar tomorrowDate = new GregorianCalendar(curDate.get(Calendar.YEAR),
38 | curDate.get(Calendar.MONTH),
39 | curDate.get(Calendar.DATE) + 1, 0, 0, 0);
40 | return tomorrowDate.getTimeInMillis() - System.currentTimeMillis();
41 | }
42 |
43 | /**
44 | * 获取缓存值
45 | *
46 | * @param key
47 | * @return
48 | */
49 | protected static Object get(String key) {
50 | String value = null;
51 | try {
52 | if (StringUtils.isNotBlank(key)) {
53 | return cache.getIfPresent(key);
54 | }
55 | } catch (Exception e) {
56 | e.printStackTrace();
57 | }
58 |
59 | return value;
60 | }
61 |
62 | /**
63 | * 获取缓存值,如果值不存在返回默认值
64 | *
65 | * @param key
66 | * @return
67 | */
68 | protected static Object get(String key, String defaultValue) {
69 | Object value = null;
70 | try {
71 | if (StringUtils.isNotBlank(key)) {
72 | value = cache.getIfPresent(key);
73 | value = value != null ? value : defaultValue;
74 | }
75 | } catch (Exception e) {
76 | e.printStackTrace();
77 | }
78 |
79 | return value;
80 | }
81 |
82 | /**
83 | * 获取缓存值,如果值不存在执行回调方法
84 | *
85 | * @param key
86 | * @param loader
87 | * @return
88 | */
89 | protected static Object get(String key, Callable loader) {
90 | Object value = null;
91 | try {
92 | if (StringUtils.isNotBlank(key) && loader != null) {
93 | return cache.get(key, loader);
94 | }
95 | } catch (Exception e) {
96 | e.printStackTrace();
97 | }
98 |
99 | return value;
100 | }
101 |
102 | /**
103 | * 放入缓存
104 | *
105 | * @param key
106 | * @param value
107 | */
108 | protected static void put(String key, Object value) {
109 | try {
110 | if (StringUtils.isNotEmpty(key) && value != null) {
111 | cache.put(key, value);
112 | }
113 | } catch (Exception e) {
114 | e.printStackTrace();
115 | }
116 | }
117 |
118 | /**
119 | * 移除缓存
120 | *
121 | * @param key
122 | */
123 | protected static void remove(String key) {
124 | try {
125 | if (StringUtils.isNotEmpty(key)) {
126 | cache.invalidate(key);
127 | }
128 | } catch (Exception e) {
129 | e.printStackTrace();
130 | }
131 | }
132 |
133 | /**
134 | * 批量移除缓存
135 | *
136 | * @param keys
137 | */
138 | protected static void remove(List keys) {
139 | try {
140 | if (!CollectionUtil.isEmpty(keys)) {
141 | cache.invalidateAll(keys);
142 | }
143 | } catch (Exception e) {
144 | e.printStackTrace();
145 | }
146 | }
147 |
148 | /**
149 | * 清空缓存
150 | */
151 | protected static void removeAll() {
152 | try {
153 | cache.invalidateAll();
154 | } catch (Exception e) {
155 | e.printStackTrace();
156 | }
157 | }
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/HashedWheelTask.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import java.util.concurrent.Executors;
4 | import java.util.concurrent.ScheduledExecutorService;
5 | import java.util.concurrent.TimeUnit;
6 |
7 | public class HashedWheelTask {
8 |
9 | /**
10 | * 调度线程池
11 | */
12 | private static ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
13 |
14 | /**
15 | * 初始化
16 | */
17 | public static void init() {
18 | service.scheduleAtFixedRate(HashedWheelUtils::task, 1, 1, TimeUnit.MINUTES);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/HashedWheelUtils.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import cn.hutool.core.collection.CollectionUtil;
4 | import cn.hutool.json.JSONUtil;
5 | import com.itkevin.common.config.SysConfig;
6 | import com.itkevin.common.constants.SysConstant;
7 | import com.itkevin.common.enums.BusinessTypeEnum;
8 | import com.itkevin.common.model.*;
9 | import com.itkevin.common.notice.NotifyMessageTools;
10 | import lombok.extern.slf4j.Slf4j;
11 | import org.apache.commons.lang3.StringUtils;
12 | import reactor.core.publisher.Mono;
13 | import reactor.core.scheduler.Schedulers;
14 |
15 | import java.util.Collections;
16 | import java.util.List;
17 | import java.util.concurrent.ExecutorService;
18 | import java.util.concurrent.LinkedBlockingQueue;
19 | import java.util.concurrent.ThreadPoolExecutor;
20 | import java.util.concurrent.TimeUnit;
21 | import java.util.concurrent.atomic.AtomicInteger;
22 |
23 | @Slf4j
24 | public class HashedWheelUtils {
25 |
26 | /**
27 | * 时间轮大小
28 | */
29 | public static final int WHEEL_SIZE = 60;
30 |
31 | /**
32 | * 时间轮当前位置
33 | */
34 | public static final String WHEEL_CURRENT_INDEX = "WHEEL_CURRENT_INDEX";
35 |
36 | /**
37 | * 时间轮延迟位置
38 | */
39 | public static final String WHEEL_WAIT_INDEX = "WHEEL_WAIT_INDEX";
40 |
41 | /**
42 | * 发送消息时间轮数据-指定线程池
43 | */
44 | private static ExecutorService executorService = new ThreadPoolExecutor(SysConstant.THREAD_NUM, SysConstant.MAX_THREAD_NUM, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1), new ThreadPoolExecutor.DiscardPolicy());
45 |
46 | /**
47 | * intGaugeNumber
48 | */
49 | private static AtomicInteger gaugeNumber = SimpleMetricsUtils.getIntGaugeNumber(SysConstant.ALARM_METRIC_NAME, Collections.emptyList(), Collections.emptyList());
50 |
51 | /**
52 | * 将任务加入到时间轮
53 | * @param hashedWheelData
54 | */
55 | public static void putWheelQueue(HashedWheelData hashedWheelData) {
56 | // 任务模型
57 | TaskModel taskModel = new TaskModel();
58 | taskModel.setBusinessId(hashedWheelData.getBusinessId());
59 | int cycleNum = 0;
60 | if (hashedWheelData.getDelayTime() > WHEEL_SIZE) {
61 | cycleNum = hashedWheelData.getDelayTime() / WHEEL_SIZE;
62 | }
63 | taskModel.setCycleNum(cycleNum);
64 | // 任务数据放入缓存
65 | LocalCacheUtils.putCache(hashedWheelData.getBusinessId(), JSONUtil.toJsonStr(hashedWheelData));
66 | // 获取时间轮当前位置
67 | Integer currentIndex = LocalCacheUtils.getIntCache(WHEEL_CURRENT_INDEX);
68 | // 计算任务存放位置
69 | int offset = hashedWheelData.getDelayTime() - WHEEL_SIZE * cycleNum;
70 | if (currentIndex + offset > WHEEL_SIZE) {
71 | currentIndex = currentIndex + offset - WHEEL_SIZE;
72 | } else {
73 | currentIndex += offset;
74 | }
75 | // 任务加入时间轮
76 | LocalCacheUtils.sadd(WHEEL_WAIT_INDEX + currentIndex, JSONUtil.toJsonStr(taskModel));
77 | }
78 |
79 | /**
80 | * 时间轮任务
81 | */
82 | protected synchronized static void task() {
83 | Integer currentIndex = LocalCacheUtils.incr(WHEEL_CURRENT_INDEX);
84 | if (currentIndex > WHEEL_SIZE) {
85 | currentIndex = currentIndex - WHEEL_SIZE;
86 | LocalCacheUtils.putIntCache(WHEEL_CURRENT_INDEX, currentIndex);
87 | }
88 | // 获取时间轮当前位置的任务列表
89 | List list = LocalCacheUtils.smember(WHEEL_WAIT_INDEX + currentIndex);
90 | if (!CollectionUtil.isEmpty(list)) {
91 | // 循环处理任务
92 | for (String task : list) {
93 | String businessId = "";
94 | try {
95 | TaskModel taskModel = JSONUtil.toBean(task, TaskModel.class);
96 | businessId = taskModel.getBusinessId();
97 | if (taskModel.getCycleNum() == 0) {
98 | String data = LocalCacheUtils.getCache(businessId);
99 | if (StringUtils.isNotBlank(data)) {
100 | HashedWheelData hashedWheelData = JSONUtil.toBean(data, HashedWheelData.class);
101 | handleHashedWheelData(hashedWheelData);
102 | LocalCacheUtils.srem(WHEEL_WAIT_INDEX + currentIndex, task);
103 | LocalCacheUtils.delCache(businessId);
104 | LocalCacheUtils.delIntCache(hashedWheelData.getBusinessId());
105 | LocalCacheUtils.delLongCache(businessId + ElapsedUtils.MAX_URI_ELAPSED);
106 | LocalCacheUtils.delCache(businessId + ElapsedUtils.MAX_URI_ELAPSED + ElapsedUtils.MAX_URI_ELAPSED_TRACEID);
107 | LocalCacheUtils.delCache(businessId + ElapsedUtils.URI_ELAPSED_TRACEID_LIST);
108 | }
109 | } else {
110 | taskModel.setCycleNum(taskModel.getCycleNum() - 1);
111 | LocalCacheUtils.srem(WHEEL_WAIT_INDEX + currentIndex, task);
112 | LocalCacheUtils.sadd(WHEEL_WAIT_INDEX + currentIndex, JSONUtil.toJsonStr(taskModel));
113 | }
114 | } catch (Exception e) {
115 | log.warn("log skyeye >>> HashedWheelTask occur exception, businessId: {}", businessId, e);
116 | LocalCacheUtils.srem(WHEEL_WAIT_INDEX + currentIndex, task);
117 | LocalCacheUtils.delCache(businessId);
118 | LocalCacheUtils.delIntCache(businessId);
119 | LocalCacheUtils.delLongCache(businessId + ElapsedUtils.MAX_URI_ELAPSED);
120 | LocalCacheUtils.delCache(businessId + ElapsedUtils.MAX_URI_ELAPSED + ElapsedUtils.MAX_URI_ELAPSED_TRACEID);
121 | LocalCacheUtils.delCache(businessId + ElapsedUtils.URI_ELAPSED_TRACEID_LIST);
122 | }
123 | }
124 | }
125 | // 异常报警上报
126 | Integer skyAlarmNum = LocalCacheUtils.getIntCache(SysConstant.ALARM_METRIC_NAME);
127 | SimpleMetricsUtils.setIntGaugeNumber(gaugeNumber, skyAlarmNum);
128 | LocalCacheUtils.putIntCache(SysConstant.ALARM_METRIC_NAME, 0);
129 | }
130 |
131 | /**
132 | * 处理时间轮任务数据
133 | * @param hashedWheelData
134 | */
135 | private static void handleHashedWheelData(HashedWheelData hashedWheelData) {
136 | String businessType = hashedWheelData.getBusinessType();
137 | if (BusinessTypeEnum.NOTIFY.name().equalsIgnoreCase(businessType)) {
138 | Integer alarmNotifyTime = SysConfig.instance.getAlarmNotifyTime();
139 | Integer alarmNotifyCount = SysConfig.instance.getAlarmNotifyCount();
140 | Integer count = LocalCacheUtils.getIntCache(hashedWheelData.getBusinessId());
141 | // 实际报警次数超过阀值则发送聚合报警消息
142 | if (count > alarmNotifyCount) {
143 | BusinessData businessData = JSONUtil.toBean(hashedWheelData.getBusinessData(), BusinessData.class);
144 | LogCompressData logCompressData = CommonConverter.getConverter().map(businessData, LogCompressData.class);
145 | logCompressData.setAlarmTime(alarmNotifyTime);
146 | logCompressData.setAlarmCount(count);
147 | Mono.fromRunnable(() -> {
148 | try {
149 | NotifyMessageTools.getInstance().sendAlarmTalk(logCompressData);
150 | } catch (Throwable e) {
151 | log.warn("log skyeye >>> HashedWheelTask.handleHashedWheelData[{}] occur exception", businessType, e);
152 | }
153 | }).subscribeOn(Schedulers.fromExecutorService(executorService, "skyeye-sendMessage-hashedWheelData-executor")).subscribe();
154 | }
155 | }
156 | if (BusinessTypeEnum.URI_ELAPSED.name().equalsIgnoreCase(businessType)) {
157 | Integer alarmUriElapsedTime = SysConfig.instance.getAlarmUriElapsedTime();
158 | Integer alarmUriElapsedCount = SysConfig.instance.getAlarmUriElapsedCount();
159 | Integer count = LocalCacheUtils.getIntCache(hashedWheelData.getBusinessId());
160 | // 耗时大于指定时间的次数超过阀值则发送聚合消息
161 | if (count > alarmUriElapsedCount) {
162 | BusinessData businessData = JSONUtil.toBean(hashedWheelData.getBusinessData(), BusinessData.class);
163 | LogUriElapsedData logUriElapsedData = CommonConverter.getConverter().map(businessData, LogUriElapsedData.class);
164 | logUriElapsedData.setAlarmTime(alarmUriElapsedTime);
165 | logUriElapsedData.setAlarmCount(count);
166 | logUriElapsedData.setUriElapsedThreshold(ElapsedUtils.getUriElapsedThreshold(businessData.getRequestURI()));
167 | logUriElapsedData.setTraceIdList(LocalCacheUtils.smember(hashedWheelData.getBusinessId() + ElapsedUtils.URI_ELAPSED_TRACEID_LIST));
168 | logUriElapsedData.setMaxUriElapsed(LocalCacheUtils.getLongCache(hashedWheelData.getBusinessId() + ElapsedUtils.MAX_URI_ELAPSED));
169 | logUriElapsedData.setMaxUriElapsedTraceId(LocalCacheUtils.getCache(hashedWheelData.getBusinessId() + ElapsedUtils.MAX_URI_ELAPSED + ElapsedUtils.MAX_URI_ELAPSED_TRACEID));
170 | Mono.fromRunnable(() -> {
171 | try {
172 | NotifyMessageTools.getInstance().sendAlarmTalk(logUriElapsedData);
173 | } catch (Throwable e) {
174 | log.warn("log skyeye >>> HashedWheelTask.handleHashedWheelData[{}] occur exception", businessType, e);
175 | }
176 | }).subscribeOn(Schedulers.fromExecutorService(executorService, "skyeye-sendMessage-hashedWheelData-executor")).subscribe();
177 | }
178 | }
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/IPUtils.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.net.InetAddress;
8 | import java.net.NetworkInterface;
9 | import java.net.UnknownHostException;
10 | import java.util.Enumeration;
11 |
12 | public class IPUtils {
13 | private static String hostName;
14 |
15 | /**
16 | * 获取本机ip
17 | * @return
18 | */
19 | public static String getLocalIp() {
20 | try {
21 | //一个主机有多个网络接口
22 | Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces();
23 | while (netInterfaces.hasMoreElements()) {
24 | NetworkInterface netInterface = netInterfaces.nextElement();
25 | //每个网络接口,都会有多个"网络地址",比如一定会有loopback地址,会有siteLocal地址等.以及IPV4或者IPV6 .
26 | Enumeration addresses = netInterface.getInetAddresses();
27 | while (addresses.hasMoreElements()) {
28 | InetAddress address = addresses.nextElement();
29 | //get only :172.*,192.*,10.*
30 | if (address.isSiteLocalAddress() && !address.isLoopbackAddress()) {
31 | return address.getHostAddress();
32 | }
33 | }
34 | }
35 | }catch (Exception e) {
36 | e.printStackTrace();
37 | return null;
38 | }
39 | return null;
40 | }
41 |
42 | /**
43 | * 获取本机hostname
44 | * @return
45 | */
46 | public static String getLocalHostName() {
47 | if (StringUtils.isNotBlank(hostName)) {
48 | return hostName;
49 | }
50 | String hostname = null;
51 | try {
52 | hostname = InetAddress.getLocalHost().getHostName();
53 | } catch (UnknownHostException uhe) {
54 | String host = uhe.getMessage();
55 | if (host != null) {
56 | int colon = host.indexOf(':');
57 | if (colon > 0) {
58 | return host.substring(0, colon);
59 | }
60 | }
61 | }
62 | if (StringUtils.isNotBlank(hostname) && hostname.contains("-")) {
63 | hostName = hostname;
64 | return hostname;
65 | }
66 | InputStream is = null;
67 | try {
68 | Process p = Runtime.getRuntime().exec("hostname");
69 | byte[] hostBytes = new byte[1024];
70 | is = p.getInputStream();
71 | int readed = is.read(hostBytes);
72 | p.waitFor();
73 | hostName = new String(hostBytes, 0, readed);
74 | if (StringUtils.isNotBlank(hostName)) {
75 | hostName = hostName.trim();
76 | }
77 | return hostName;
78 | } catch (Throwable e) {
79 | e.printStackTrace();
80 | } finally {
81 | if (is != null) {
82 | try {
83 | is.close();
84 | } catch (IOException e) {
85 | e.printStackTrace();
86 | }
87 | }
88 | }
89 | hostName = "unknownHostname";
90 | return hostName;
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/JobHandlerAdvisor.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import com.ctrip.framework.foundation.Foundation;
4 | import com.itkevin.common.enums.MDCConstantEnum;
5 | import com.itkevin.common.enums.RequestTypeEnum;
6 | import lombok.extern.slf4j.Slf4j;
7 | import net.bytebuddy.asm.Advice;
8 | import org.apache.skywalking.apm.toolkit.trace.TraceContext;
9 |
10 | import java.lang.reflect.Method;
11 |
12 | @Slf4j
13 | public class JobHandlerAdvisor {
14 |
15 | @Advice.OnMethodEnter
16 | public static void onMethodEnter(@Advice.This Object object, @Advice.Origin Method method, @Advice.AllArguments Object[] arguments) {
17 | try {
18 | String requestParam = arguments != null && arguments.length > 0 ? (String) arguments[0] : "";
19 | MDCUtils.put(MDCConstantEnum.SERVER_NAME.getCode(), Foundation.app().getAppId());
20 | MDCUtils.put(MDCConstantEnum.SERVER_IP.getCode(), IPUtils.getLocalIp());
21 | MDCUtils.put(MDCConstantEnum.SERVER_HOSTNAME.getCode(), IPUtils.getLocalHostName());
22 | MDCUtils.put(MDCConstantEnum.REQUEST_TYPE.getCode(), RequestTypeEnum.JOB.name().toLowerCase());
23 | MDCUtils.put(MDCConstantEnum.TRACE_ID.getCode(), TraceContext.traceId());
24 | MDCUtils.put(MDCConstantEnum.REQUEST_URI.getCode(), object.getClass().getName() + "#execute");
25 | MDCUtils.put(MDCConstantEnum.REQUEST_PARAM.getCode(), requestParam);
26 | } catch (Exception e) {
27 | log.warn("log skyeye >>> JobHandlerAdvisor.onMethodEnter occur exception", e);
28 | }
29 | }
30 |
31 | @Advice.OnMethodExit(onThrowable = Throwable.class)
32 | public static void onMethodExit() {
33 | MDCUtils.clear();
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/LocalCacheUtils.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import cn.hutool.json.JSONUtil;
4 | import com.itkevin.common.model.TaskModel;
5 | import org.apache.commons.lang3.StringUtils;
6 |
7 | import java.util.List;
8 | import java.util.concurrent.CopyOnWriteArrayList;
9 | import java.util.concurrent.atomic.AtomicInteger;
10 | import java.util.concurrent.atomic.AtomicLong;
11 | import java.util.concurrent.atomic.AtomicReference;
12 |
13 | public class LocalCacheUtils {
14 |
15 | /**
16 | * int前缀
17 | */
18 | private static final String KEY_INT = "LOG_SKYEYE_INT_";
19 |
20 | /**
21 | * alarm前缀
22 | */
23 | private static final String KEY_ALARM = "LOG_SKYEYE_ALARM_";
24 |
25 | /**
26 | * 对key的value值做加1操作
27 | * @param key
28 | * @return
29 | */
30 | public static Integer incr(String key) {
31 | if (StringUtils.isBlank(key)) {
32 | return 0;
33 | }
34 | try {
35 | key = KEY_INT + key;
36 | AtomicInteger atomicInteger = (AtomicInteger) GuavaCacheUtils.get(key, AtomicInteger::new);
37 | return atomicInteger.incrementAndGet();
38 | } catch (Exception e) {
39 | e.printStackTrace();
40 | }
41 | return 0;
42 | }
43 |
44 | /**
45 | * 存储int缓存
46 | */
47 | public static void putIntCache(String key, Integer value) {
48 | try {
49 | if (StringUtils.isNotBlank(key) && value != null) {
50 | key = KEY_INT + key;
51 | GuavaCacheUtils.put(key, new AtomicInteger(value));
52 | }
53 | } catch (Exception e) {
54 | e.printStackTrace();
55 | }
56 | }
57 |
58 | /**
59 | * 获取int缓存
60 | */
61 | public static Integer getIntCache(String key) {
62 | try {
63 | if (StringUtils.isNotBlank(key)) {
64 | key = KEY_INT + key;
65 | AtomicInteger atomicInteger = (AtomicInteger) GuavaCacheUtils.get(key, AtomicInteger::new);
66 | return atomicInteger.get();
67 | }
68 | } catch (Exception e) {
69 | e.printStackTrace();
70 | }
71 | return 0;
72 | }
73 |
74 | /**
75 | * 删除int缓存
76 | * @param key
77 | */
78 | protected static void delIntCache(String key) {
79 | try {
80 | if (StringUtils.isNotBlank(key)) {
81 | key = KEY_INT + key;
82 | GuavaCacheUtils.remove(key);
83 | }
84 | } catch (Exception e) {
85 | e.printStackTrace();
86 | }
87 | }
88 |
89 | /**
90 | * 存储long缓存
91 | */
92 | protected static void putLongCache(String key, Long value) {
93 | try {
94 | if (StringUtils.isNotBlank(key) && value != null) {
95 | key = KEY_INT + key;
96 | GuavaCacheUtils.put(key, new AtomicLong(value));
97 | }
98 | } catch (Exception e) {
99 | e.printStackTrace();
100 | }
101 | }
102 |
103 | /**
104 | * 获取long缓存
105 | */
106 | protected static Long getLongCache(String key) {
107 | try {
108 | if (StringUtils.isNotBlank(key)) {
109 | key = KEY_INT + key;
110 | AtomicLong atomicLong = (AtomicLong) GuavaCacheUtils.get(key, AtomicLong::new);
111 | return atomicLong.get();
112 | }
113 | } catch (Exception e) {
114 | e.printStackTrace();
115 | }
116 | return 0L;
117 | }
118 |
119 | /**
120 | * 删除long缓存
121 | * @param key
122 | */
123 | protected static void delLongCache(String key) {
124 | try {
125 | if (StringUtils.isNotBlank(key)) {
126 | key = KEY_INT + key;
127 | GuavaCacheUtils.remove(key);
128 | }
129 | } catch (Exception e) {
130 | e.printStackTrace();
131 | }
132 | }
133 |
134 | /**
135 | * 存储缓存
136 | * @param key
137 | * @param value
138 | */
139 | protected static void putCache(String key, String value) {
140 | try {
141 | if (StringUtils.isNotBlank(key) && value != null) {
142 | key = KEY_ALARM + key;
143 | GuavaCacheUtils.put(key, new AtomicReference<>(value));
144 | }
145 | } catch (Exception e) {
146 | e.printStackTrace();
147 | }
148 | }
149 |
150 | /**
151 | * 获取缓存
152 | * @param key
153 | * @return
154 | */
155 | protected static String getCache(String key) {
156 | try {
157 | if (StringUtils.isNotBlank(key)) {
158 | key = KEY_ALARM + key;
159 | AtomicReference> atomicReference = (AtomicReference>) GuavaCacheUtils.get(key, AtomicReference::new);
160 | Object object = atomicReference.get();
161 | if (object instanceof String) {
162 | return atomicReference.get().toString();
163 | }
164 | }
165 | } catch (Exception e) {
166 | e.printStackTrace();
167 | }
168 | return null;
169 | }
170 |
171 | /**
172 | * 删除缓存
173 | * @param key
174 | */
175 | protected static void delCache(String key) {
176 | try {
177 | if (StringUtils.isNotBlank(key)) {
178 | key = KEY_ALARM + key;
179 | GuavaCacheUtils.remove(key);
180 | }
181 | } catch (Exception e) {
182 | e.printStackTrace();
183 | }
184 | }
185 |
186 | /**
187 | * 存储集合缓存
188 | * @param key
189 | * @param value
190 | */
191 | protected static void sadd(String key, String value) {
192 | try {
193 | if (StringUtils.isNotBlank(key) && value != null) {
194 | key = KEY_ALARM + key;
195 | CopyOnWriteArrayList list = (CopyOnWriteArrayList) GuavaCacheUtils.get(key, CopyOnWriteArrayList::new);
196 | list.add(value);
197 | }
198 | } catch (Exception e) {
199 | e.printStackTrace();
200 | }
201 | }
202 |
203 | /**
204 | * 获取集合缓存
205 | * @param key
206 | * @return
207 | */
208 | protected static List smember(String key) {
209 | try {
210 | if (StringUtils.isNotBlank(key)) {
211 | key = KEY_ALARM + key;
212 | return (CopyOnWriteArrayList) GuavaCacheUtils.get(key, CopyOnWriteArrayList::new);
213 | }
214 | } catch (Exception e) {
215 | e.printStackTrace();
216 | }
217 | return null;
218 | }
219 |
220 | /**
221 | * 删除集合缓存元素
222 | * @param key
223 | * @param value
224 | */
225 | protected static void srem(String key, String value) {
226 | try {
227 | if (StringUtils.isNotBlank(key) && value != null) {
228 | key = KEY_ALARM + key;
229 | CopyOnWriteArrayList list = (CopyOnWriteArrayList) GuavaCacheUtils.get(key, CopyOnWriteArrayList::new);
230 | list.removeIf(s -> s.equals(value));
231 | }
232 | } catch (Exception e) {
233 | e.printStackTrace();
234 | }
235 | }
236 |
237 | public static void main(String[] args) {
238 | TaskModel taskModel = new TaskModel();
239 | taskModel.setBusinessId("hashcode");
240 | taskModel.setCycleNum(1);
241 | sadd("key", JSONUtil.toJsonStr(taskModel));
242 | sadd("key", JSONUtil.toJsonStr(taskModel));
243 | System.out.println(smember("key"));
244 | srem("key", JSONUtil.toJsonStr(taskModel));
245 | System.out.println(smember("key"));
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/LogUtils.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import com.ctrip.framework.foundation.Foundation;
4 | import com.itkevin.common.config.SysConfig;
5 | import com.itkevin.common.constants.SysConstant;
6 | import com.itkevin.common.enums.LogLevelEnum;
7 | import com.itkevin.common.enums.MDCConstantEnum;
8 | import com.itkevin.common.model.FilterMessage;
9 | import com.itkevin.common.model.LogData;
10 | import org.apache.commons.lang3.StringUtils;
11 |
12 | import java.util.regex.Matcher;
13 | import java.util.regex.Pattern;
14 |
15 | public class LogUtils {
16 |
17 | /**
18 | * 异常过滤
19 | *
20 | * @param filterMessage
21 | * @return
22 | */
23 | public static boolean filter(FilterMessage filterMessage) {
24 | String logType = filterMessage.getLogType();
25 | String msg = filterMessage.getMsg();
26 | String message = filterMessage.getMessage();
27 | String stackTrace = filterMessage.getStackTrace();
28 | // 特殊error信息过滤
29 | if (StringUtils.isNotBlank(msg)) {
30 | // rocketmq打印的error过滤掉
31 | if (msg.contains("consume topic:") && msg.contains("consumer:") && msg.contains("msg:") && msg.contains("msgId:") && msg.contains("bornTimestamp:")
32 | || msg.contains("topic:") && msg.contains("consumer:") && msg.contains("msgSize:"))
33 | return true;
34 | }
35 | // 获取配置
36 | String alarmWhiteList = SysConfig.instance.getAlarmWhiteList();
37 |
38 | return filter(msg, alarmWhiteList) || filter(message, alarmWhiteList) || filter(stackTrace, alarmWhiteList);
39 | }
40 |
41 | /**
42 | * 获取LogData数据对象
43 | *
44 | * @param logType
45 | * @param msg
46 | * @param message
47 | * @param stackTrace
48 | * @return
49 | */
50 | public static LogData obtainLogData(String logType, String msg, String message, String stackTrace) {
51 | // 获取配置
52 | int stackNum = SysConfig.instance.getAlarmStacknum();
53 | // logData
54 | LogData logData = new LogData();
55 | logData.setLevel(getLogLevel(msg));
56 | msg = StringUtils.isNotBlank(msg) ? msg.replaceAll("header(.*)", "").trim() : "";
57 | msg = StringUtils.isNotBlank(msg) ? msg.replaceAll("headers(.*)", "").trim() : "";
58 | logData.setErrorMessage(msg);
59 | logData.setServerName(Foundation.app().getAppId());
60 | logData.setServerIP(IPUtils.getLocalIp());
61 | logData.setServerHostname(IPUtils.getLocalHostName());
62 | logData.setSourceIP(MDCUtils.get(MDCConstantEnum.SOURCE_IP.getCode()));
63 | logData.setRequestType(MDCUtils.get(MDCConstantEnum.REQUEST_TYPE.getCode()));
64 | logData.setTraceId(MDCUtils.get(MDCConstantEnum.TRACE_ID.getCode()));
65 | logData.setRequestURI(MDCUtils.get(MDCConstantEnum.REQUEST_URI.getCode()));
66 | logData.setRequestParam(MDCUtils.get(MDCConstantEnum.REQUEST_PARAM.getCode()));
67 | logData.setMessageTopic(MDCUtils.get(MDCConstantEnum.MESSAGE_TOPIC.getCode()));
68 | logData.setMessageId(MDCUtils.get(MDCConstantEnum.MESSAGE_ID.getCode()));
69 | logData.setMessageKeys(MDCUtils.get(MDCConstantEnum.MESSAGE_KEYS.getCode()));
70 | logData.setEventName(MDCUtils.get(MDCConstantEnum.EVENT_NAME.getCode()));
71 | logData.setEventPayload(MDCUtils.get(MDCConstantEnum.EVENT_PAYLOAD.getCode()));
72 | logData.setExceptionMessage(message);
73 | String exceptionStackTrace = getRegexContent(stackTrace, stackNum);
74 | if (!exceptionStackTrace.contains("Caused by")) {
75 | String causedByContent = getCausedByContentOfStackTrace(stackTrace, 1);
76 | exceptionStackTrace += causedByContent;
77 | }
78 | String regex = "(dubbo)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";
79 | exceptionStackTrace = exceptionStackTrace.replaceAll(regex,"XXX");
80 | logData.setExceptionStackTrace(exceptionStackTrace);
81 |
82 | return logData;
83 | }
84 |
85 | /**
86 | * 信息过滤
87 | *
88 | * @param msg
89 | * @param filter
90 | * @return
91 | */
92 | public static boolean filter(String msg, String filter) {
93 | if (StringUtils.isNotBlank(msg) && StringUtils.isNotBlank(filter)) {
94 | String[] filters = filter.split(",");
95 | for (String filt : filters) {
96 | if (msg.contains(filt)) {
97 | return true;
98 | }
99 | }
100 | }
101 |
102 | return false;
103 | }
104 |
105 | /**
106 | * 日志级别
107 | *
108 | * @param msg
109 | * @return
110 | */
111 | private static String getLogLevel(String msg) {
112 | String level = LogLevelEnum.NORMAL.name();
113 | if (StringUtils.isNotBlank(msg)) {
114 | return msg.toLowerCase().contains("level") && msg.toLowerCase().contains("serious") ? LogLevelEnum.SERIOUS.name() : LogLevelEnum.NORMAL.name();
115 | }
116 |
117 | return level;
118 | }
119 |
120 | /**
121 | * 字符串正则截取
122 | *
123 | * @param content
124 | * @param seq
125 | * @return
126 | */
127 | private static String getRegexContent(String content, int seq) {
128 | Matcher slashMatcher = Pattern.compile(System.getProperty("line.separator")).matcher(content);
129 | int mIdx = 0;
130 | do {
131 | if (!slashMatcher.find()) {
132 | return content;
133 | }
134 | ++mIdx;
135 | } while(mIdx != seq);
136 |
137 | return content.substring(0, slashMatcher.start());
138 | }
139 |
140 | /**
141 | * 截取异常堆栈的Caused by内容
142 | *
143 | * @param stackTrace
144 | * @param stackNum
145 | * @return
146 | */
147 | private static String getCausedByContentOfStackTrace(String stackTrace, int stackNum) {
148 | String content = "";
149 | if (stackTrace.contains("Caused by")) {
150 | content = System.getProperty("line.separator") + getRegexContent(stackTrace.substring(stackTrace.indexOf("Caused by")), stackNum);
151 | }
152 |
153 | return content;
154 | }
155 |
156 | }
157 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/MD5Utils.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import java.security.MessageDigest;
4 | import java.security.NoSuchAlgorithmException;
5 |
6 | public class MD5Utils {
7 |
8 | /**
9 | * md5加密
10 | * @param plainText
11 | * @return
12 | */
13 | public static String encryption(String plainText) {
14 | String re_md5 = new String();
15 | try {
16 | MessageDigest md = MessageDigest.getInstance("MD5");
17 | md.update(plainText.getBytes());
18 | byte b[] = md.digest();
19 | int i;
20 | StringBuffer buf = new StringBuffer("");
21 | for (int offset = 0; offset < b.length; offset++) {
22 | i = b[offset];
23 | if (i < 0)
24 | i += 256;
25 | if (i < 16)
26 | buf.append("0");
27 | buf.append(Integer.toHexString(i));
28 | }
29 | re_md5 = buf.toString();
30 | } catch (NoSuchAlgorithmException e) {
31 | e.printStackTrace();
32 | }
33 | return re_md5;
34 | }
35 |
36 | public static void main(String[] args) {
37 | String str = "";
38 | String resultStr = encryption(str);
39 | System.out.println("MD5加密结果:"+resultStr);
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/MDCUtils.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import cn.hutool.core.collection.CollectionUtil;
4 | import org.apache.commons.lang3.StringUtils;
5 | import org.slf4j.MDC;
6 |
7 | import java.util.Map;
8 |
9 | public class MDCUtils {
10 |
11 | /**
12 | * MDC 获取数据
13 | * @param key
14 | * @return
15 | */
16 | public static String get(String key) {
17 | String value = null;
18 | try {
19 | if (StringUtils.isNotBlank(key)) {
20 | value = MDC.get(key);
21 | }
22 | } catch (Exception e) {
23 | e.printStackTrace();
24 | }
25 |
26 | return value;
27 | }
28 |
29 | /**
30 | * MDC 获取数据
31 | * @param key
32 | * @param defaultValue
33 | * @return
34 | */
35 | public static String get(String key,String defaultValue) {
36 | String value = null;
37 | try {
38 | if (StringUtils.isNotBlank(key)) {
39 | value = MDC.get(key);
40 | value = value != null ? value : defaultValue;
41 | }
42 | } catch (Exception e) {
43 | e.printStackTrace();
44 | }
45 |
46 | return value;
47 | }
48 |
49 | /**
50 | * MDC put数据
51 | * @param key
52 | * @param value
53 | */
54 | public static void put(String key,String value) {
55 | try {
56 | if (StringUtils.isNotEmpty(key) && value != null) {
57 | MDC.put(key,value);
58 | }
59 | } catch (Exception e) {
60 | e.printStackTrace();
61 | }
62 | }
63 |
64 | /**
65 | * 复制一份MDC
66 | * @return
67 | */
68 | public static Map getCopyMDC() {
69 | try {
70 | return MDC.getCopyOfContextMap();
71 | } catch (Exception e) {
72 | e.printStackTrace();
73 | return null;
74 | }
75 | }
76 |
77 | /**
78 | * 设置MDC
79 | * @param mdc
80 | */
81 | public static void setMDC(Map mdc) {
82 | try {
83 | if (!CollectionUtil.isEmpty(mdc)) {
84 | MDC.setContextMap(mdc);
85 | }
86 | } catch (Exception e) {
87 | e.printStackTrace();
88 | }
89 | }
90 |
91 | /**
92 | * MDC删除
93 | * @param key
94 | */
95 | public static void remove(String key) {
96 | try {
97 | if (StringUtils.isNotBlank(key)) {
98 | MDC.remove(key);
99 | }
100 | } catch (Exception e) {
101 | e.printStackTrace();
102 | }
103 | }
104 |
105 | /**
106 | * MDC清理
107 | */
108 | public static void clear() {
109 | try {
110 | MDC.clear();
111 | } catch (Exception e) {
112 | e.printStackTrace();
113 | }
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/PropertiesUtils.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.apache.commons.lang3.StringUtils;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | import java.io.FileInputStream;
9 | import java.io.InputStream;
10 | import java.net.URL;
11 | import java.util.ArrayList;
12 | import java.util.Enumeration;
13 | import java.util.List;
14 | import java.util.Properties;
15 |
16 | @Slf4j
17 | public class PropertiesUtils {
18 |
19 | private static final Logger logger = LoggerFactory.getLogger(PropertiesUtils.class);
20 |
21 | private PropertiesUtils() {
22 | }
23 |
24 | private static volatile Properties PROPERTIES;
25 |
26 | public static final String SYSTEM_GLOBAL = "SystemGlobals.properties";
27 |
28 | public static Properties getProperties() {
29 | if (PROPERTIES == null) {
30 | synchronized (PropertiesUtils.class) {
31 | if (PROPERTIES == null) {
32 | String path = System.getProperty(SYSTEM_GLOBAL);
33 | if (path == null || path.length() == 0) {
34 | path = System.getenv(SYSTEM_GLOBAL);
35 | if (path == null || path.length() == 0) {
36 | path = SYSTEM_GLOBAL;
37 | }
38 | }
39 | PROPERTIES = loadProperties(path, false, true);
40 | }
41 | }
42 | }
43 | return PROPERTIES;
44 | }
45 |
46 | public static void addProperties(Properties properties) {
47 | if (properties != null) {
48 | getProperties().putAll(properties);
49 | }
50 | }
51 |
52 | public static void setProperties(Properties properties) {
53 | if (properties != null) {
54 | PROPERTIES = properties;
55 | }
56 | }
57 |
58 | public static String getProperty(String key) {
59 | return getProperty(key, null);
60 | }
61 |
62 | public static String getProperty(String key, String defaultValue) {
63 | String value = System.getProperty(key);
64 | if (value != null && value.trim().length() == 0) {
65 | return null;
66 | }
67 | if (value != null) {
68 | return value;
69 | }
70 | Properties properties = getProperties();
71 | value = properties.getProperty(key);
72 | if (StringUtils.isNotBlank(value)) {
73 | return value;
74 | } else {
75 | return defaultValue;
76 | }
77 | }
78 |
79 | public static Properties loadProperties(String fileName) {
80 | return loadProperties(fileName, false, false);
81 | }
82 |
83 | public static Properties loadProperties(String fileName, boolean allowMultiFile) {
84 | return loadProperties(fileName, allowMultiFile, false);
85 | }
86 |
87 | public static Properties loadProperties(String fileName, boolean allowMultiFile, boolean optional) {
88 | Properties properties = new Properties();
89 | if (fileName.startsWith("/")) {
90 | try {
91 | FileInputStream input = new FileInputStream(fileName);
92 | try {
93 | properties.load(input);
94 | }
95 | finally {
96 | input.close();
97 | }
98 | }
99 | catch (Throwable e) {
100 | logger.warn(
101 | "Failed to load " + fileName + " file from " + fileName + "(ingore this file): "
102 | + e.getMessage(), e);
103 | }
104 | return properties;
105 | }
106 |
107 | List list = new ArrayList();
108 | try {
109 | Enumeration urls = PropertiesUtils.class.getClassLoader().getResources(fileName);
110 | list = new ArrayList();
111 | while (urls.hasMoreElements()) {
112 | list.add(urls.nextElement());
113 | }
114 | }
115 | catch (Throwable t) {
116 | logger.warn("Fail to load " + fileName + " file: " + t.getMessage(), t);
117 | }
118 |
119 | if (list.size() == 0) {
120 | if (!optional) {
121 | logger.warn("No " + fileName + " found on the class path.");
122 | }
123 | return properties;
124 | }
125 |
126 | if (!allowMultiFile) {
127 | if (list.size() > 1) {
128 | String errMsg = String.format(
129 | "only 1 %s file is expected, but %d dubbo.properties files found on class path: %s", fileName,
130 | list.size(), list.toString());
131 | logger.warn(errMsg);
132 | // throw new IllegalStateException(errMsg); // see
133 | // http://code.alibabatech.com/jira/browse/DUBBO-133
134 | }
135 |
136 | // fall back to use method getResourceAsStream
137 | try {
138 | properties.load(PropertiesUtils.class.getClassLoader().getResourceAsStream(fileName));
139 | }
140 | catch (Throwable e) {
141 | logger.warn(
142 | "Failed to load " + fileName + " file from " + fileName + "(ingore this file): "
143 | + e.getMessage(), e);
144 | }
145 | return properties;
146 | }
147 |
148 | logger.info("load " + fileName + " properties file from " + list);
149 |
150 | for (URL url : list) {
151 | try {
152 | Properties p = new Properties();
153 | InputStream input = url.openStream();
154 | if (input != null) {
155 | try {
156 | p.load(input);
157 | properties.putAll(p);
158 | }
159 | finally {
160 | try {
161 | input.close();
162 | }
163 | catch (Throwable t) {}
164 | }
165 | }
166 | }
167 | catch (Throwable e) {
168 | logger.warn("Fail to load " + fileName + " file from " + url + "(ingore this file): " + e.getMessage(),
169 | e);
170 | }
171 | }
172 |
173 | return properties;
174 | }
175 |
176 | public static void setProperty(String key, String value) {
177 |
178 | Properties properties = getProperties();
179 | properties.setProperty(key, value);
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/SimpleMetricsUtils.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import com.ctrip.framework.apollo.core.enums.Env;
4 | import com.ctrip.framework.foundation.Foundation;
5 | import com.google.common.collect.ImmutableList;
6 | import io.micrometer.core.instrument.Metrics;
7 | import io.micrometer.core.instrument.Tags;
8 | import lombok.extern.slf4j.Slf4j;
9 | import org.apache.commons.lang3.ArrayUtils;
10 |
11 | import java.util.Collection;
12 | import java.util.List;
13 | import java.util.concurrent.atomic.AtomicInteger;
14 | import java.util.stream.IntStream;
15 |
16 | @Slf4j
17 | public class SimpleMetricsUtils {
18 |
19 | /**
20 | * 累计计数
21 | * @param name
22 | * @param tags
23 | * @param values
24 | * @param amount
25 | */
26 | public static void counter(String name, List tags, List values, Double amount) {
27 | try {
28 | if (filter()) return;
29 | String[] tagValues = getTagValues(tags, values);
30 | // Monitors.count(name, amount, tagValues);
31 | } catch (Exception e) {
32 | log.warn("log skyeye >>> SimpleMetricsUtils.counter occur exception", e);
33 | }
34 | }
35 |
36 | /**
37 | * 获取intGaugeNumber
38 | * @param name
39 | * @param tags
40 | * @param values
41 | * @return
42 | */
43 | public static AtomicInteger getIntGaugeNumber(String name, List tags, List values) {
44 | AtomicInteger number = new AtomicInteger(0);
45 | try {
46 | if (filter()) return number;
47 | String[] tagValues = getTagValues(tags, values);
48 | number = Metrics.gauge(name, Tags.of(tagValues), new AtomicInteger(0));
49 | } catch (Exception e) {
50 | log.warn("log skyeye >>> SimpleMetricsUtils.getIntGaugeNumber occur exception", e);
51 | }
52 | return number;
53 | }
54 |
55 | /**
56 | * 设置intGaugeNumber
57 | * @param number
58 | * @param amount
59 | */
60 | public static void setIntGaugeNumber(AtomicInteger number, Integer amount) {
61 | try {
62 | if (filter()) return;
63 | number.set(amount);
64 | } catch (Exception e) {
65 | log.warn("log skyeye >>> SimpleMetricsUtils.setIntGaugeNumber occur exception", e);
66 | }
67 | }
68 |
69 | /**
70 | * 过滤上报
71 | * @return
72 | */
73 | private static boolean filter() {
74 | boolean filter = false;
75 | String envName = Foundation.server().getEnvType();
76 | // if (Env.YUFA.name().equalsIgnoreCase(envName)) {
77 | // filter = true;
78 | // }
79 | return filter;
80 | }
81 |
82 | /**
83 | * 获取标签数组
84 | * @param tags
85 | * @param values
86 | * @return
87 | */
88 | private static String[] getTagValues(List tags, List values) {
89 | return ArrayUtils.toStringArray(
90 | IntStream.range(0, Math.min(tags.size(), values.size())).mapToObj(index -> ImmutableList.of(tags.get(index), values.get(index)))
91 | .flatMap(Collection::stream).toArray()
92 | );
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/common/src/main/java/com/itkevin/common/util/StringConverterFactory.java:
--------------------------------------------------------------------------------
1 | package com.itkevin.common.util;
2 |
3 | import okhttp3.MediaType;
4 | import okhttp3.RequestBody;
5 | import okhttp3.ResponseBody;
6 | import retrofit2.Converter;
7 | import retrofit2.Retrofit;
8 |
9 | import java.lang.annotation.Annotation;
10 | import java.lang.reflect.Type;
11 |
12 | /**
13 | * retrofit2 String转换器
14 | */
15 | public class StringConverterFactory extends Converter.Factory {
16 |
17 | @Override
18 | public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
19 | return (Converter) ResponseBody::string;
20 | }
21 |
22 | @Override
23 | public Converter, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
24 | return (Converter) value -> RequestBody.create(MediaType.parse("text/plain"), value);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/common/src/main/resources/META-INF/services/com.itkevin.common.config.ConfigTool:
--------------------------------------------------------------------------------
1 | com.itkevin.common.config.ApolloConfigTool
--------------------------------------------------------------------------------
/common/src/main/resources/META-INF/services/com.itkevin.common.notice.NoticeInterface:
--------------------------------------------------------------------------------
1 | com.itkevin.common.notice.dingding.DingTalkNotice
2 | com.itkevin.common.notice.workwx.WorkWeiXinTalkNotice
--------------------------------------------------------------------------------
/common/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/common/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | logback
5 |
6 |
7 |
8 |
9 | %d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
10 |
11 |
12 |
13 |
14 | ${log.path}
15 |
16 | ${log.path}.%d{yyyy-MM-dd}.zip
17 |
18 |
19 | %date %level [%thread] %logger{36} [%file : %line] %msg%n
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/img/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevin0016/sky-eye/c667f5f785859eccdc50f67c31cedd16a56b4358/img/img.png
--------------------------------------------------------------------------------
/img/主要特性.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevin0016/sky-eye/c667f5f785859eccdc50f67c31cedd16a56b4358/img/主要特性.png
--------------------------------------------------------------------------------
/img/报警种类.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevin0016/sky-eye/c667f5f785859eccdc50f67c31cedd16a56b4358/img/报警种类.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.itkevin
8 | sky-eye
9 | pom
10 | 2.0
11 |
12 | api
13 | common
14 | common-web
15 | common-dubbo
16 |
17 |
18 |
19 | 8
20 | 8
21 |
22 |
23 |
24 |
25 | com.squareup.okhttp3
26 | okhttp
27 | 3.14.4
28 |
29 |
30 |
31 | org.jsoup
32 | jsoup
33 | 1.12.1
34 |
35 |
36 |
37 |
38 | cn.hutool
39 | hutool-all
40 | 5.3.2
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/userManual.md:
--------------------------------------------------------------------------------
1 | ###### 1、引入包
2 | ```xml
3 |
4 |
5 | com.itkevin
6 | log4j-api
7 | 1.0.0
8 |
9 |
10 |
11 |
12 | com.itkevin
13 | logback-api
14 | 1.0.0
15 |
16 | ```
17 |
18 | ###### 2、设置设置启动参数
19 | ```java
20 | // log4j
21 | com.itkevin.log4j.api.listener.Log4jApplicationListener
22 | // logback
23 | com.itkevin.logback.api.listener.LogbackApplicationListener
24 | // 以上两个类交由spring 管理
25 | ```
26 | ###### 3、apollo配置参数
27 | 首先需要创建对应的namespace名称为:skyeye
28 | ```properties
29 | # 是否启动报警
30 | skyeye.log.alarm.enabled = true
31 | # 报警钉钉严重错误机器人配置(支持多个机器人)
32 | skyeye.log.alarm.serious.dingtalk = [ { "webHook": "https://oapi.dingtalk.com/robot/send?access_token=xxxxx", "secret": "xxxx" } ]
33 | # 报警钉钉机器人配置(支持多个机器人)
34 | skyeye.log.alarm.dingtalk = [ { "webHook": "https://oapi.dingtalk.com/robot/send?access_token=xxx", "secret": "xxxx" } ]
35 | # 堆栈行数配置
36 | skyeye.log.alarm.stackNum = 10
37 | # 单条报警白名单
38 | skyeye.log.alarm.white.list = 我是白名单
39 | # 聚合报警白名单
40 | skyeye.log.alarm.aggre.white.list = 我是聚合白名单
41 | # 报警间隔时间(单位分钟)
42 | skyeye.log.alarm.notify.time = 1
43 | # 报警次数阀值
44 | skyeye.log.alarm.notify.count = 1
45 | # 接口耗时报警间隔时间(单位分钟)
46 | skyeye.log.alarm.uri.elapsed.time = 1
47 | # 接口耗时超过阀值时间的次数阀值(阀值时间如果不指定则默认1000毫秒)
48 | skyeye.log.alarm.uri.elapsed.count = 10
49 | # 指定URI接口耗时时间阀值(单位毫秒,支持指定多个URI)
50 | skyeye.log.alarm.uri.elapsed = [{"uri":"/user/logTest","elapsed":2000}]
51 | # 指定接口耗时时间阀值(单位毫秒,全局指定,不配置默认1000毫秒)
52 | skyeye.log.alarm.uri.elapsed.global = 1000
53 | ```
54 | ###### 备注
55 | - 目前接口超时报警仅支持web,后期会同步支持部分的rpc框架
56 | - 目前仅支持apollo配置和钉钉报警,后期会开放配置接口,可自行选择配置,报警接口也会支持企业微信和飞书,并且开放通知接口,可自定接入报警
57 |
58 |
--------------------------------------------------------------------------------