├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
└── main
├── java
└── cn
│ └── keking
│ └── common
│ └── log
│ ├── Constants.java
│ ├── LogAppenderAutoConfiguration.java
│ ├── LogEnhancerBinder.java
│ ├── appender
│ ├── AliYunAppenderCallback.java
│ ├── log4j2
│ │ └── Log4j2AliYunAppender.java
│ └── logback
│ │ └── LogbackAliYunAppender.java
│ ├── config
│ ├── CommonLogAppenderConfigLoader.java
│ ├── CommonProjectConfig.java
│ ├── LogAppenderConfig.java
│ └── LogAppenderConfigLoader.java
│ ├── enhancer
│ ├── AbstractLogEnhancer.java
│ ├── DefaultLog4j2Enhancer.java
│ ├── DefaultLogbackEnhancer.java
│ ├── LogEnhancer.java
│ └── PatternLayoutEncoder.java
│ └── utils
│ └── LogEnvUtils.java
└── resources
├── META-INF
└── spring.factories
└── aliyunlog.properties
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 | .sts4-cache
12 |
13 | ### IntelliJ IDEA ###
14 | .idea
15 | *.iws
16 | *.iml
17 | *.ipr
18 |
19 | ### NetBeans ###
20 | /nbproject/private/
21 | /build/
22 | /nbbuild/
23 | /dist/
24 | /nbdist/
25 | /.nb-gradle/
26 |
27 | # for Mac OS X System Files
28 | .DS_Store
29 | Thumbs.db
30 |
31 | # Compiled class file
32 | *.class
33 |
34 | # Log file
35 | *.log
36 |
37 | # BlueJ files
38 | *.ctxt
39 |
40 | # Mobile Tools for Java (J2ME)
41 | .mtj.tmp/
42 |
43 | # Package Files #
44 | *.jar
45 | *.war
46 | *.ear
47 | *.zip
48 | *.tar.gz
49 | *.rar
50 |
51 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
52 | hs_err_pid*
--------------------------------------------------------------------------------
/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 | # kk-aliyun-log-appender
2 | `kk-aliyun-log-appender`能够自动感知应用环境内引入的的日志实现(Log4j2及Logback)并自动接入阿里云日志服务,在保留灵活配置的同时,大幅度简化开发配置,让应用适配日志服务更简单。
3 |
4 | ## 一、使用场景
5 | 1. 简化繁琐的阿里云日志服务接入的配置,`kk-aliyun-log-appender`已集成不同日志框架的阿里云日志服务接入实现,且能自动感知引入的日志实现,无需根据不同的日志实现去配置不同的接入逻辑。
6 | 2. 提供了默认配置,大多数场景下仅需要配置topic属性即可接入。
7 | 3. 提供了灵活的配置方案。
8 | 4. 接入了新版[Aliyun LOG Java Producer](https://github.com/aliyun/aliyun-log-java-producer)
9 |
10 | > Aliyun LOG Java Producer 是对老版 log-loghub-producer 的全面升级,解决了上一版存在的多个问题,包括网络异常情况下 CPU 占用率过高、关闭 producer 可能出现少量数据丢失等问题。另外,在容错方面也进行了加强,即使您存在误用,在资源、吞吐、隔离等方面都有较好的保证。
11 | 5. 对原有的log4j2.xml或logback.xml中的配置无影响
12 |
13 | ## 二、快速开始
14 | ### 2.1 springboot项目接入
15 | 1.添加maven依赖
16 | ```xml
17 |
18 | com.github.kekingcn
19 | aliyunlog-spring-boot-starter
20 | 0.1-SNAPSHOT
21 |
22 | ```
23 | 2.配置必备属性topic
24 |
25 | topic用以标记一批日志,例如可以通过配置topic属性来区分不同的应用及应用profile(kk-call-center-prod)
26 | ```properties
27 | aliyun.log.enable=true
28 | aliyun.log.topic=test-topic
29 | ```
30 | 3.`kk-aliyun-log-appender`默认为所有的logger接入阿里云日志服务,且日志级别为logger上定义的日志级别。
31 |
32 | 4.启动应用
33 | 如果接入成功,则会打印如下log
34 | ```
35 | [main] INFO cn.keking.common.log.LogAppenderAutoConfiguration - kk aliyun log appender has been successfully initialized
36 | ```
37 | ### 2.2 spring mvc项目接入
38 | 和spring boot相似,需要在spring-xx.xml配置中添加类扫描即可
39 | ```xml
40 |
41 | ```
42 |
43 | ## 三、配置参数
44 |
45 | 仅aliyun.log.topic为必填属性
46 |
47 | | 参数 | 类型 | 默认值 | 说明 |
48 | | :--- | :---: | :---: | --- |
49 | | aliyun.log.enable | boolean | false | 是否上传至阿里云日志 |
50 | | **aliyun.log.topic** | String | 无 | 日志主题,**必填** |
51 | | aliyun.log.charset | String | UTF-8 | 输出到日志服务的字符集,默认是 UTF-8 |
52 | | aliyun.log.timeZone | String | UTC | 输出到日志服务的时间的时区,默认是 UTC |
53 | | aliyun.log.timeFormat | String | yyyy-MM-dd'T'HH:mmZ | 输出到日志服务的时间的格式 |
54 | | aliyun.log.logger.filter | List | 空 | 日志logger过滤器 |
55 | | aliyun.log.pattern | String | %d{HH:mm:ss.SSS} \[%thread\] %-5level %logger{36} - %msg%n | 自定义上传日志pattern |
56 | | aliyun.log.project.name | String | kk-log-proj | 日志服务的 project 名 |
57 | | aliyun.log.project.logstore | String | apps-shanghai | 日志服务的 logstore 名 |
58 | | aliyun.log.project.endpoint | String | cn-shanghai-intranet.log.aliyuncs.com | 日志服务的 HTTP 地址 |
59 | | aliyun.log.project.accessKeyId | String | LTAIC***SO32Qy | 用户身份标识 |
60 | | aliyun.log.project.accessKeySecret | String | csvZDMv******DiyaTaRzal | 用户身份标识 |
61 | | aliyun.log.project.stsToken | String | null | 为RAM角色签发的STS Token来访问阿里云服务 |
62 | | aliyun.log.project.userAgent | String | aliyun-log-java-producer | userAgent |
63 | | aliyun.log.producer.totalSizeInBytes | int | 100 * 1024 * 1024 | 单个producer实例能缓存的日志大小上限,默认为100MB。 |
64 | | aliyun.log.producer.maxBlockMs | long | 60*1000 | 如果producer可用空间不足,调用者在send方法上的最大阻塞时间,默认为60秒。如果超过这个时间后所需空间仍无法得到满足,send方法会抛出TimeoutException。如果将该值设为0,当所需空间无法得到满足时,send方法会立即抛出TimeoutException。如果您希望send方法一直阻塞直到所需空间得到满足,可将该值设为负数。 |
65 | | aliyun.log.producer.ioThreadCount | int | availableProcessors | 执行日志发送任务的线程池大小,默认为可用处理器个数。 |
66 | | aliyun.log.producer.batchSizeThresholdInBytes | int | 512 * 1024 | 当一个ProducerBatch中缓存的日志大小大于等于batchSizeThresholdInBytes时,该batch将被发送,默认为512KB,最大可设置成5MB。 |
67 | | aliyun.log.producer.batchCountThreshold | int | 4096 | 当一个ProducerBatch中缓存的日志条数大于等于batchCountThreshold时,该batch将被发送,默认为4096,最大可设置成40960。 |
68 | | aliyun.log.producer.lingerMs | int | 2000 | 一个ProducerBatch从创建到可发送的逗留时间,默认为2秒,最小可设置成100毫秒。 |
69 | | aliyun.log.producer.retries | int | 10 | 如果某个ProducerBatch首次发送失败,能够对其重试的次数,默认为10次。如果retries小于等于0,该ProducerBatch首次发送失败后将直接进入失败队列。 |
70 | | aliyun.log.producer.maxReservedAttempts | int | 11 | 每个ProducerBatch每次被尝试发送都对应着一个Attempt,此参数用来控制返回给用户的attempt个数,默认只保留最近的11次attempt信息。该参数越大能让您追溯更多的信息,但同时也会消耗更多的内存。 |
71 | | aliyun.log.producer.baseRetryBackoffMs | long | 100 | 默认值 | 首次重试的退避时间,默认为100毫秒。Producer采样指数退避算法,第N次重试的计划等待时间为baseRetryBackoffMs*2^(N-1)。 |
72 | | aliyun.log.producer.maxRetryBackoffMs | long | 50 * 1000 | 重试的最大退避时间,默认为50秒。 |
73 | | aliyun.log.producer.adjustShardHash | boolean | true | 如果调用send方法时指定了shardHash,该参数用于控制是否需要对其进行调整,默认为true。 |
74 | | aliyun.log.producer.buckets | int | 64 | 当且仅当adjustShardHash为true时,该参数才生效。此时,producer会自动将shardHash重新分组,分组数量为buckets。如果两条数据的shardHash不同,它们是无法合并到一起发送的,会降低producer吞吐量。将shardHash重新分组后,能让数据有更多地机会被批量发送。该参数的取值范围是[1,256],且必须是2的整数次幂,默认为64。 |
75 | 参阅:[Aliyun LOG Java Producer](https://github.com/aliyun/aliyun-log-java-producer)
76 |
77 | ## 四、使用限制
78 | 1. jdk`1.8`
79 | 2. Spring`4`以上
80 | 3. 需要统一日志编程接口至slf4j
81 | 4. 应用内的日志框架logback `1.2.3`版本以上或log4j2 `2.0.2`版本以上(与原阿里SDK一致)
82 | 5. 如果已接入阿里SDK,则本日志组件将不会起任何作用
83 |
84 | ## 五、常见场景
85 | #### 5.1 开发环境不上传日志到阿里云
86 | 在application-dev.properties中配置关闭上传日志
87 | ```properties
88 | aliyun.log.enable=false
89 | ```
90 | 该参数默认为false,在生产环境中应当设置enable=true
91 | #### 5.2 自定义logger上传日志
92 | 例如在log4j2中有如下的配置
93 | ```xml
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | ```
106 | 如果想要达到只有自定义的**mylog1**的log上传至阿里云
107 | 可以在application.properties中配置
108 | ```properties
109 | aliyun.log.logger.filter=ROOT,mylog2
110 | ```
111 | #### 5.3 关闭日志发送失败重试
112 | 可以在application.properties中配置
113 | ```properties
114 | aliyun.log.producer.reties=0
115 | ```
116 | #### 5.4 配置上传日志pattern
117 | 可以在application.properties中配置
118 | ```properties
119 | aliyun.log.pattern=%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
120 | ```
121 | 格式参阅:
122 | [log4j2](https://logging.apache.org/log4j/2.x/manual/layouts.html)
123 | [logback](https://logback.qos.ch/manual/layouts.html)
124 | #### 5.5 整合skywalking traceId
125 | 已集成skywalking toolkit,log4j2请使用\[%traceId],logback请使用\[%tid]
126 | ```properties
127 | aliyun.log.pattern=%d{HH:mm:ss.SSS} [%traceId] [%t] %-5level %logger{36} - %msg%n
128 | ```
129 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.github.kekingcn
8 | aliyunlog-spring-boot-starter
9 | 0.1-SNAPSHOT
10 |
11 | aliyunlog 快速接入的 spring boot starter组件
12 |
13 |
14 | UTF-8
15 | 1.8
16 | 1.8
17 | 4.3.19.RELEASE
18 | 1.5.16.RELEASE
19 | 1.9.2
20 | 0.2.0
21 | 0.6.31
22 | 2.5.0
23 | 1.2.3
24 | 1.7.25
25 | 2.13.3
26 | 2.9.9
27 | 5.0.0-alpha
28 |
29 |
30 |
31 | https://github.com/kekingcn/aliyunlog-spring-boot-starter.git
32 |
33 |
34 | https://github.com/kekingcn/aliyunlog-spring-boot-starter/issues
35 |
36 |
37 |
38 | wanglaomo
39 | wjlcoder@outlook.com
40 | https://github.com/wanglaomo
41 |
42 |
43 | kl
44 | 632104866@QQ.com
45 | https://github.com/klboke
46 | http://www.kailing.pub/
47 |
48 |
49 |
50 |
51 |
52 | org.springframework
53 | spring-context
54 | ${spring-context.version}
55 | provided
56 |
57 |
58 | org.springframework
59 | spring-web
60 | ${spring-context.version}
61 | provided
62 | true
63 |
64 |
65 | javax.servlet
66 | javax.servlet-api
67 | 4.0.1
68 | provided
69 | true
70 |
71 |
72 | org.springframework.boot
73 | spring-boot
74 | ${spring-boot.version}
75 | provided
76 |
77 |
78 |
79 |
80 | ch.qos.logback
81 | logback-classic
82 | ${logback.version}
83 | provided
84 |
85 |
86 |
87 | ch.qos.logback
88 | logback-core
89 | ${logback.version}
90 | provided
91 |
92 |
93 |
94 |
95 | org.slf4j
96 | slf4j-api
97 | ${slf4j.version}
98 | provided
99 |
100 |
101 |
102 |
103 | org.apache.logging.log4j
104 | log4j-api
105 | ${log4j2.version}
106 | provided
107 |
108 |
109 | org.apache.logging.log4j
110 | log4j-core
111 | ${log4j2.version}
112 | provided
113 |
114 |
115 |
116 |
117 | com.aliyun.openservices
118 | aliyun-log-producer
119 | ${aliyun-log-producer.version}
120 |
121 |
122 | com.aliyun.openservices
123 | aliyun-log
124 | ${aliyun-log.version}
125 |
126 |
127 |
128 |
129 | org.apache.skywalking
130 | apm-toolkit-logback-1.x
131 | ${skywalking.toolkit.version}
132 |
133 |
134 | org.apache.skywalking
135 | apm-toolkit-log4j-2.x
136 | ${skywalking.toolkit.version}
137 |
138 |
139 |
140 |
141 | commons-beanutils
142 | commons-beanutils
143 | ${commons-beanutils.version}
144 |
145 |
146 | com.google.protobuf
147 | protobuf-java
148 | ${protobuf-java.version}
149 |
150 |
151 | joda-time
152 | joda-time
153 | ${joda-time.version}
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | repo
162 | http://ops.keking.cn:8081/nexus/content/repositories/snapshots
163 |
164 |
165 | repo
166 | http://ops.keking.cn:8081/nexus/content/repositories/releases
167 |
168 |
169 |
170 |
171 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/Constants.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log;
2 |
3 | /**
4 | *
5 | */
6 | public interface Constants {
7 |
8 | String LOG_CONFIG_PREFIX = "aliyun.log";
9 |
10 | String LOG_PROJECT_CONFIG_PREFIX = LOG_CONFIG_PREFIX + ".project";
11 |
12 | String LOG_PRODUCER_CONFIG_PREFIX = LOG_CONFIG_PREFIX + ".producer";
13 |
14 | String LOG_CONFIG_LOGGER_FILTER = "logger.filter";
15 |
16 | String DEFAULT_ROOT_LOGGER_NAME = "ROOT";
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/LogAppenderAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log;
2 | import cn.keking.common.log.config.CommonLogAppenderConfigLoader;
3 | import cn.keking.common.log.config.LogAppenderConfig;
4 | import cn.keking.common.log.config.LogAppenderConfigLoader;
5 | import cn.keking.common.log.enhancer.LogEnhancer;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.context.EnvironmentAware;
9 | import org.springframework.context.annotation.Configuration;
10 | import org.springframework.core.env.ConfigurableEnvironment;
11 | import org.springframework.core.env.Environment;
12 |
13 |
14 | /**
15 | * @Author wanglaomo
16 | * @Date 2019/4/9
17 | **/
18 | @Configuration
19 | public class LogAppenderAutoConfiguration implements EnvironmentAware {
20 |
21 | private static final Logger logger = LoggerFactory.getLogger(LogAppenderAutoConfiguration.class);
22 | @Override
23 | public void setEnvironment(Environment environment) {
24 |
25 | logger.info("kk aliyun log appender has been integrated into system");
26 |
27 | ConfigurableEnvironment env = (ConfigurableEnvironment)environment;
28 |
29 | // load config
30 | LogAppenderConfigLoader loader = new CommonLogAppenderConfigLoader();
31 | final LogAppenderConfig config;
32 | try {
33 | config = loader.load(env);
34 | } catch (Exception e) {
35 | throw new IllegalStateException("Failed to load log appender config", e);
36 | }
37 |
38 | if(!config.isEnable()) {
39 | return;
40 | }
41 |
42 | // init logEnhancer
43 | ClassLoader classLoader = env.getClass().getClassLoader();
44 | LogEnhancerBinder.bindClassLoader(classLoader);
45 | LogEnhancer logEnhancer = LogEnhancerBinder.getInstance();
46 |
47 | // do nothing if aliyun appender has been bound
48 | if(!logEnhancer.alreadyBound()) {
49 |
50 | logEnhancer.enhance(config);
51 | logger.info("kk aliyun log appender has been successfully initialized");
52 | }
53 | }
54 |
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/LogEnhancerBinder.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log;
2 |
3 | import cn.keking.common.log.enhancer.DefaultLog4j2Enhancer;
4 | import cn.keking.common.log.enhancer.DefaultLogbackEnhancer;
5 | import cn.keking.common.log.enhancer.LogEnhancer;
6 | import cn.keking.common.log.utils.LogEnvUtils;
7 |
8 | /**
9 | * @Author wanglaomo
10 | * @Date 2019/4/3
11 | **/
12 | public class LogEnhancerBinder {
13 |
14 | private volatile static LogEnhancer INSTANCE;
15 |
16 | private static ClassLoader classLoader = ClassLoader.getSystemClassLoader();
17 |
18 |
19 | public static void bindClassLoader(ClassLoader classLoader) {
20 |
21 | if(INSTANCE == null) {
22 |
23 | synchronized (LogEnhancerBinder.class) {
24 |
25 | if(INSTANCE == null) {
26 | LogEnhancerBinder.classLoader = classLoader;
27 | }
28 | }
29 | }
30 | }
31 |
32 | public static LogEnhancer getInstance() {
33 |
34 | if(INSTANCE == null) {
35 |
36 | synchronized (LogEnhancerBinder.class) {
37 |
38 | if(INSTANCE == null) {
39 |
40 | INSTANCE = doInit(classLoader);
41 | }
42 |
43 | }
44 | }
45 |
46 | return INSTANCE;
47 | }
48 |
49 | private static LogEnhancer doInit(ClassLoader classLoader) {
50 |
51 | LogEnhancer enhancer = null;
52 | if(LogEnvUtils.isLog4j2Usable(classLoader)) {
53 | enhancer = new DefaultLog4j2Enhancer(classLoader);
54 | } else if(LogEnvUtils.isLogbackUsable(classLoader)) {
55 | enhancer = new DefaultLogbackEnhancer(classLoader);
56 | } else {
57 | throw new IllegalStateException("No applicable logging system found");
58 | }
59 |
60 | return enhancer;
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/appender/AliYunAppenderCallback.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log.appender;
2 |
3 | import cn.keking.common.log.config.LogAppenderConfig;
4 | import com.aliyun.openservices.aliyun.log.producer.Callback;
5 | import com.aliyun.openservices.aliyun.log.producer.Result;
6 | import com.aliyun.openservices.log.common.LogItem;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import java.util.List;
11 |
12 |
13 | /**
14 | * Common Aliyun appender producer callback
15 | *
16 | * @Author wanglaomo
17 | * @Date 2019/4/3
18 | **/
19 | public class AliYunAppenderCallback implements Callback {
20 |
21 | private final Logger logger = LoggerFactory.getLogger(AliYunAppenderCallback.class);
22 |
23 | private final String project;
24 |
25 | private final String logStore;
26 |
27 | private final String topic;
28 |
29 | private final String source;
30 |
31 | private final List logItems;
32 |
33 | public AliYunAppenderCallback(LogAppenderConfig config, List logItems) {
34 | super();
35 | this.project = config.getProjectConfig().getName();
36 | this.logStore = config.getProjectConfig().getLogstore();
37 | this.topic = config.getTopic();
38 | this.source = config.getSource();
39 | this.logItems = logItems;
40 | }
41 |
42 | @Override
43 | public void onCompletion(Result result) {
44 |
45 | if (!result.isSuccessful()) {
46 | logger.error("Failed to putLogs. project=" + project + " logStore=" + logStore +
47 | " topic=" + topic + " source=" + source + " logItems=" + logItems, result);
48 | }
49 |
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/appender/log4j2/Log4j2AliYunAppender.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log.appender.log4j2;
2 |
3 | import cn.keking.common.log.appender.AliYunAppenderCallback;
4 | import cn.keking.common.log.config.LogAppenderConfig;
5 | import com.aliyun.openservices.aliyun.log.producer.*;
6 | import com.aliyun.openservices.aliyun.log.producer.errors.LogSizeTooLargeException;
7 | import com.aliyun.openservices.aliyun.log.producer.errors.TimeoutException;
8 | import com.aliyun.openservices.log.common.LogItem;
9 | import org.apache.logging.log4j.core.LogEvent;
10 | import org.apache.logging.log4j.core.appender.AbstractAppender;
11 | import org.apache.logging.log4j.core.layout.PatternLayout;
12 | import org.apache.logging.log4j.core.util.Throwables;
13 | import org.joda.time.DateTime;
14 | import org.joda.time.DateTimeZone;
15 | import org.joda.time.format.DateTimeFormat;
16 | import org.joda.time.format.DateTimeFormatter;
17 |
18 | import java.util.ArrayList;
19 | import java.util.Arrays;
20 | import java.util.List;
21 | import java.util.Map;
22 |
23 |
24 | /**
25 | * Aliyun appender for log4j2
26 | *
27 | * @Author wanglaomo
28 | * @Date 2019/4/3
29 | **/
30 | public class Log4j2AliYunAppender extends AbstractAppender {
31 |
32 | public static final String APPENDER_NAME = "ALI_YUN_APPENDER";
33 |
34 | private Producer producer;
35 |
36 | private final LogAppenderConfig config;
37 |
38 | protected DateTimeFormatter formatter;
39 |
40 | public Log4j2AliYunAppender(LogAppenderConfig config) {
41 |
42 | super(APPENDER_NAME, null, null);
43 | this.config = config;
44 | }
45 |
46 |
47 | public Log4j2AliYunAppender(LogAppenderConfig config, PatternLayout layout) {
48 |
49 | super(APPENDER_NAME, null, layout);
50 | this.config = config;
51 | }
52 |
53 | @Override
54 | public void start() {
55 | super.start();
56 |
57 | formatter = DateTimeFormat.forPattern(config.getTimeFormat()).withZone(DateTimeZone.forID(config.getTimeZone()));
58 | ProducerConfig producerConfig = config.getProducerConfig();
59 | if (producerConfig == null) {
60 | ProjectConfigs projectConfigs = new ProjectConfigs();
61 | projectConfigs.put(config.getProjectConfig().buildProjectConfig());
62 | producerConfig = new ProducerConfig(projectConfigs);
63 | }
64 | producer = new LogProducer(producerConfig);
65 | }
66 |
67 | @Override
68 | public void stop() {
69 | super.stop();
70 | if (producer != null) {
71 | try {
72 | producer.close();
73 | } catch (Exception e) {
74 | LOGGER.error("Failed to close aliyun log producer: ", e);
75 | }
76 | }
77 | }
78 |
79 | @Override
80 | public void append(LogEvent event) {
81 |
82 | List logItems = new ArrayList();
83 | LogItem item = new LogItem();
84 | logItems.add(item);
85 | item.SetTime((int) (event.getTimeMillis() / 1000));
86 | DateTime dateTime = new DateTime(event.getTimeMillis());
87 | item.PushBack("time", dateTime.toString(formatter));
88 | item.PushBack("level", event.getLevel().toString());
89 | item.PushBack("thread", event.getThreadName());
90 |
91 | StackTraceElement source = event.getSource();
92 | if (source == null && (!event.isIncludeLocation())) {
93 | event.setIncludeLocation(true);
94 | source = event.getSource();
95 | event.setIncludeLocation(false);
96 | }
97 |
98 | item.PushBack("location", source == null ? "Unknown(Unknown Source)" : source.toString());
99 |
100 | String message = event.getMessage().getFormattedMessage();
101 | item.PushBack("message", message);
102 |
103 | String throwable = getThrowableStr(event.getThrown());
104 | if (throwable != null) {
105 | item.PushBack("throwable", throwable);
106 | }
107 |
108 | if (getLayout() != null) {
109 | item.PushBack("log", new String(getLayout().toByteArray(event)));
110 | }
111 |
112 | Map properties = event.getContextMap();
113 | if (properties.size() > 0) {
114 | Object[] keys = properties.keySet().toArray();
115 | Arrays.sort(keys);
116 | for (int i = 0; i < keys.length; i++) {
117 | item.PushBack(keys[i].toString(), properties.get(keys[i].toString()));
118 | }
119 | }
120 |
121 | try {
122 | producer.send(
123 | config.getProjectConfig().getName(),
124 | config.getProjectConfig().getLogstore(),
125 | config.getTopic(),
126 | null,
127 | logItems,
128 | new AliYunAppenderCallback(config, logItems));
129 | } catch (InterruptedException e) {
130 | LOGGER.warn("The current thread has been interrupted during send logs.");
131 | } catch (Exception e) {
132 | if (e instanceof LogSizeTooLargeException) {
133 | LOGGER.error("The size of log is larger than the maximum allowable size, e={}", e);
134 | } else if (e instanceof TimeoutException) {
135 | LOGGER.error("The time taken for allocating memory for the logs has surpassed., e={}", e);
136 | } else {
137 | LOGGER.error("Failed to send log, logItems={}, e=", logItems, e);
138 | }
139 | }
140 | }
141 |
142 | private String getThrowableStr(Throwable throwable) {
143 | if (throwable == null) {
144 | return null;
145 | }
146 | StringBuilder sb = new StringBuilder();
147 | boolean isFirst = true;
148 | for (String s : Throwables.toStringList(throwable)) {
149 | if (isFirst) {
150 | isFirst = false;
151 | } else {
152 | sb.append(System.getProperty("line.separator"));
153 | }
154 | sb.append(s);
155 | }
156 | return sb.toString();
157 | }
158 |
159 | public LogAppenderConfig getConfig() {
160 | return config;
161 | }
162 |
163 |
164 | }
165 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/appender/logback/LogbackAliYunAppender.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log.appender.logback;
2 |
3 | import ch.qos.logback.classic.spi.IThrowableProxy;
4 | import ch.qos.logback.classic.spi.LoggingEvent;
5 | import ch.qos.logback.classic.spi.StackTraceElementProxy;
6 | import ch.qos.logback.classic.spi.ThrowableProxyUtil;
7 | import ch.qos.logback.core.CoreConstants;
8 | import ch.qos.logback.core.UnsynchronizedAppenderBase;
9 | import ch.qos.logback.core.encoder.Encoder;
10 | import cn.keking.common.log.appender.AliYunAppenderCallback;
11 | import cn.keking.common.log.config.LogAppenderConfig;
12 | import com.aliyun.openservices.aliyun.log.producer.LogProducer;
13 | import com.aliyun.openservices.aliyun.log.producer.ProducerConfig;
14 | import com.aliyun.openservices.aliyun.log.producer.ProjectConfigs;
15 | import com.aliyun.openservices.aliyun.log.producer.errors.LogSizeTooLargeException;
16 | import com.aliyun.openservices.aliyun.log.producer.errors.ProducerException;
17 | import com.aliyun.openservices.aliyun.log.producer.errors.TimeoutException;
18 | import com.aliyun.openservices.log.common.LogItem;
19 | import org.joda.time.DateTime;
20 | import org.joda.time.DateTimeZone;
21 | import org.joda.time.format.DateTimeFormat;
22 | import org.joda.time.format.DateTimeFormatter;
23 |
24 | import java.util.ArrayList;
25 | import java.util.List;
26 |
27 | /**
28 | * @ClassName LogbackAliYunAppender
29 | * @Description
30 | * @Author wanglaomo
31 | * @Date 2019/4/2
32 | **/
33 | public class LogbackAliYunAppender extends UnsynchronizedAppenderBase {
34 |
35 | private Encoder encoder;
36 |
37 | protected LogAppenderConfig config;
38 |
39 | private LogProducer producer;
40 |
41 | private DateTimeFormatter formatter;
42 |
43 | public LogbackAliYunAppender() {
44 | }
45 |
46 | public LogbackAliYunAppender(LogAppenderConfig logAppenderConfig) {
47 |
48 | this.config = logAppenderConfig;
49 | }
50 |
51 | @Override
52 | public void start() {
53 | try {
54 | doStart();
55 | } catch (Exception e) {
56 | addError("Failed to start LogbackAliYunAppender.", e);
57 | }
58 | }
59 |
60 | private void doStart() {
61 |
62 | formatter = DateTimeFormat.forPattern(config.getTimeFormat()).withZone(DateTimeZone.forID(config.getTimeZone()));
63 | if(config.getProducerConfig() == null){
64 | ProjectConfigs projectConfigs = new ProjectConfigs();
65 | projectConfigs.put(config.getProjectConfig().buildProjectConfig());
66 | ProducerConfig producerConfig = new ProducerConfig(projectConfigs);
67 | config.setProducerConfig(producerConfig);
68 | }
69 | producer = new LogProducer(config.getProducerConfig());
70 |
71 | super.start();
72 | }
73 |
74 | @Override
75 | public void stop() {
76 | try {
77 | doStop();
78 | } catch (Exception e) {
79 | addError("Failed to stop LogbackAliYunAppender.", e);
80 | }
81 | }
82 |
83 | private void doStop() throws InterruptedException, ProducerException {
84 | if (!isStarted()){
85 | return;
86 | }
87 | super.stop();
88 | if (producer != null) {
89 | producer.close();
90 | }
91 | }
92 |
93 | @Override
94 | public void append(E eventObject) {
95 |
96 | //init Event Object
97 | if (!(eventObject instanceof LoggingEvent)) {
98 | return;
99 | }
100 | LoggingEvent event = (LoggingEvent) eventObject;
101 |
102 | List logItems = new ArrayList();
103 | LogItem item = new LogItem();
104 |
105 |
106 | logItems.add(item);
107 | item.SetTime((int) (event.getTimeStamp() / 1000));
108 |
109 | DateTime dateTime = new DateTime(event.getTimeStamp());
110 | item.PushBack("time", dateTime.toString(formatter));
111 | item.PushBack("level", event.getLevel().toString());
112 | item.PushBack("thread", event.getThreadName());
113 |
114 | StackTraceElement[] caller = event.getCallerData();
115 | if (caller != null && caller.length > 0) {
116 | item.PushBack("location", caller[0].toString());
117 | }
118 |
119 | String message = event.getFormattedMessage();
120 | item.PushBack("message", message);
121 |
122 | IThrowableProxy iThrowableProxy = event.getThrowableProxy();
123 | if (iThrowableProxy != null) {
124 | String throwable = getExceptionInfo(iThrowableProxy);
125 | throwable += fullDump(event.getThrowableProxy().getStackTraceElementProxyArray());
126 | item.PushBack("throwable", throwable);
127 | }
128 |
129 | if (this.encoder != null) {
130 | item.PushBack("log", new String(this.encoder.encode(eventObject)));
131 | }
132 |
133 | try {
134 | producer.send(
135 | config.getProjectConfig().getName(),
136 | config.getProjectConfig().getLogstore(),
137 | config.getTopic(),
138 | null,
139 | logItems,
140 | new AliYunAppenderCallback(config, logItems));
141 | } catch (InterruptedException e) {
142 | addError("The current thread has been interrupted during send logs.");
143 | } catch (Exception e) {
144 | if (e instanceof LogSizeTooLargeException) {
145 | addError("The size of log is larger than the maximum allowable size, e={}", e);
146 | } else if (e instanceof TimeoutException) {
147 | addError("The time taken for allocating memory for the logs has surpassed., e={}", e);
148 | } else {
149 | addError("Failed to send log, e=", e);
150 | }
151 | }
152 | }
153 |
154 | private String getExceptionInfo(IThrowableProxy iThrowableProxy) {
155 | String s = iThrowableProxy.getClassName();
156 | String message = iThrowableProxy.getMessage();
157 | return (message != null) ? (s + ": " + message) : s;
158 | }
159 |
160 | private String fullDump(StackTraceElementProxy[] stackTraceElementProxyArray) {
161 | StringBuilder builder = new StringBuilder();
162 | for (StackTraceElementProxy step : stackTraceElementProxyArray) {
163 | builder.append(CoreConstants.LINE_SEPARATOR);
164 | String string = step.toString();
165 | builder.append(CoreConstants.TAB).append(string);
166 | ThrowableProxyUtil.subjoinPackagingData(builder, step);
167 | }
168 | return builder.toString();
169 | }
170 |
171 | public LogAppenderConfig getConfig() {
172 | return config;
173 | }
174 |
175 | public void setConfig(LogAppenderConfig config) {
176 | this.config = config;
177 | }
178 |
179 | public void setEncoder(Encoder encoder) {
180 | this.encoder = encoder;
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/config/CommonLogAppenderConfigLoader.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log.config;
2 |
3 | import cn.keking.common.log.Constants;
4 | import com.aliyun.openservices.aliyun.log.producer.ProducerConfig;
5 | import com.aliyun.openservices.aliyun.log.producer.ProjectConfigs;
6 | import org.apache.commons.beanutils.BeanUtils;
7 | import org.apache.commons.lang.StringUtils;
8 | import org.springframework.core.env.ConfigurableEnvironment;
9 | import org.springframework.core.env.EnumerablePropertySource;
10 | import org.springframework.core.env.PropertySource;
11 |
12 | import java.lang.reflect.InvocationTargetException;
13 | import java.util.*;
14 |
15 | /**
16 | * Load log appender config from environment
17 | *
18 | * @Author wanglaomo
19 | * @Date 2019/4/8
20 | **/
21 | public class CommonLogAppenderConfigLoader implements LogAppenderConfigLoader {
22 |
23 | @Override
24 | public LogAppenderConfig load(ConfigurableEnvironment environment) throws InvocationTargetException, IllegalAccessException {
25 |
26 | LogAppenderConfig config = readConfigFromContext(environment);
27 |
28 | return config;
29 | }
30 |
31 | private LogAppenderConfig readConfigFromContext(ConfigurableEnvironment environment) throws InvocationTargetException, IllegalAccessException {
32 |
33 | Set configKeys = new HashSet<>();
34 | Iterator> propertySourceIterator = environment.getPropertySources().iterator();
35 | while (propertySourceIterator.hasNext()) {
36 | PropertySource propertySource = propertySourceIterator.next();
37 | if (propertySource instanceof EnumerablePropertySource) {
38 | configKeys.addAll(Arrays.asList(((EnumerablePropertySource) propertySource)
39 | .getPropertyNames()));
40 | }
41 | }
42 |
43 | Map context = new HashMap<>();
44 | Map projectContext = new HashMap<>();
45 | Map producerContext = new HashMap<>();
46 | for(String key : configKeys) {
47 | if(filterAllLogConfig(key)) {
48 | if(key.startsWith(Constants.LOG_PRODUCER_CONFIG_PREFIX)) {
49 | producerContext.put(
50 | parseConfigKey(Constants.LOG_PRODUCER_CONFIG_PREFIX, key),
51 | environment.getProperty(key));
52 | } else if(key.startsWith(Constants.LOG_PROJECT_CONFIG_PREFIX)) {
53 | projectContext.put(
54 | parseConfigKey(Constants.LOG_PROJECT_CONFIG_PREFIX, key),
55 | environment.getProperty(key));
56 | } else {
57 | context.put(
58 | parseConfigKey(Constants.LOG_CONFIG_PREFIX, key),
59 | environment.getProperty(key));
60 | }
61 | }
62 | }
63 |
64 | LogAppenderConfig config = new LogAppenderConfig();
65 | BeanUtils.populate(config, context);
66 | String filterStr = context.get(Constants.LOG_CONFIG_LOGGER_FILTER);
67 | if(!StringUtils.isBlank(filterStr)) {
68 | List loggerFilter = Arrays.asList(filterStr.split(","));
69 | config.getLoggerFilter().addAll(loggerFilter);
70 | }
71 | if(StringUtils.isBlank(config.getTopic())) {
72 | throw new IllegalStateException("Topic must not be null");
73 | }
74 |
75 | CommonProjectConfig commonProjectConfig = CommonProjectConfig.instance();
76 | BeanUtils.populate(commonProjectConfig, projectContext);
77 |
78 | ProjectConfigs projectConfigs = new ProjectConfigs();
79 | projectConfigs.put(commonProjectConfig.buildProjectConfig());
80 | ProducerConfig producerConfig = new ProducerConfig(projectConfigs);
81 | BeanUtils.populate(producerConfig, producerContext);
82 |
83 | config.setProducerConfig(producerConfig);
84 | config.setProjectConfig(commonProjectConfig);
85 |
86 | return config;
87 | }
88 |
89 | private String parseConfigKey(String prefix, String key) {
90 | return key.replace(prefix+".", "");
91 | }
92 |
93 | private boolean filterAllLogConfig(String key) {
94 |
95 | return key.startsWith(Constants.LOG_CONFIG_PREFIX);
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/config/CommonProjectConfig.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log.config;
2 |
3 | import com.aliyun.openservices.aliyun.log.producer.ProjectConfig;
4 | import org.springframework.core.io.support.PropertiesLoaderUtils;
5 |
6 | import java.io.IOException;
7 | import java.util.Properties;
8 |
9 | /**
10 | * @Author wanglaomo
11 | * @Date 2019/4/8
12 | **/
13 | public class CommonProjectConfig {
14 |
15 | private static final String CONFIG_FILENAME = "aliyunlog.properties";
16 |
17 | private static final String DEFAULT_LOGSTORE = "logstore";
18 |
19 | private static final String DEFAULT_PROJECT_NAME = "name";
20 |
21 | private static final String DEFAULT_ENDPOINT = "endpoint";
22 |
23 | private static final String DEFAULT_ACCESSKEY_ID = "accessKeyId";
24 |
25 | private static final String DEFAULT_ACCESSKEY_SECRET = "accessKeySecret";
26 |
27 | private static final String DEFAULT_STS_TOKEN = "stsToken";
28 |
29 | private static final String DEFAULT_USER_AGENT = "userAgent";
30 |
31 | private String name;
32 |
33 | private String logstore;
34 |
35 | private String endpoint;
36 |
37 | private String accessKeyId;
38 |
39 | private String accessKeySecret;
40 |
41 | private String stsToken;
42 |
43 | private String userAgent;
44 |
45 | private final static CommonProjectConfig COMMON_PROJECT_CONFIG = new CommonProjectConfig();
46 |
47 | private CommonProjectConfig() {
48 | try {
49 | Properties properties = PropertiesLoaderUtils.loadAllProperties(CONFIG_FILENAME, CommonProjectConfig.class.getClassLoader());
50 | this.name = properties.getProperty(DEFAULT_PROJECT_NAME);
51 | this.logstore = properties.getProperty(DEFAULT_LOGSTORE);
52 | this.endpoint = properties.getProperty(DEFAULT_ENDPOINT);
53 | this.accessKeyId = properties.getProperty(DEFAULT_ACCESSKEY_ID);
54 | this.accessKeySecret = properties.getProperty(DEFAULT_ACCESSKEY_SECRET);
55 | this.stsToken = properties.getProperty(DEFAULT_STS_TOKEN);
56 | this.userAgent = properties.getProperty(DEFAULT_USER_AGENT);
57 | }catch (IOException ex){
58 | ex.printStackTrace();
59 | }
60 | }
61 |
62 | public static CommonProjectConfig instance(){
63 | return COMMON_PROJECT_CONFIG;
64 | }
65 |
66 | public ProjectConfig buildProjectConfig() {
67 |
68 | return new ProjectConfig(name, endpoint, accessKeyId, accessKeySecret, stsToken, userAgent);
69 | }
70 |
71 | public String getLogstore() {
72 | return logstore;
73 | }
74 |
75 | public void setLogstore(String logstore) {
76 | this.logstore = logstore;
77 | }
78 |
79 | public String getName() {
80 | return name;
81 | }
82 |
83 | public void setName(String name) {
84 | this.name = name;
85 | }
86 |
87 | public String getEndpoint() {
88 | return endpoint;
89 | }
90 |
91 | public void setEndpoint(String endpoint) {
92 | this.endpoint = endpoint;
93 | }
94 |
95 | public String getAccessKeyId() {
96 | return accessKeyId;
97 | }
98 |
99 | public void setAccessKeyId(String accessKeyId) {
100 | this.accessKeyId = accessKeyId;
101 | }
102 |
103 | public String getAccessKeySecret() {
104 | return accessKeySecret;
105 | }
106 |
107 | public void setAccessKeySecret(String accessKeySecret) {
108 | this.accessKeySecret = accessKeySecret;
109 | }
110 |
111 | public String getStsToken() {
112 | return stsToken;
113 | }
114 |
115 | public void setStsToken(String stsToken) {
116 | this.stsToken = stsToken;
117 | }
118 |
119 | public String getUserAgent() {
120 | return userAgent;
121 | }
122 |
123 | public void setUserAgent(String userAgent) {
124 | this.userAgent = userAgent;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/config/LogAppenderConfig.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log.config;
2 |
3 | import com.aliyun.openservices.aliyun.log.producer.ProducerConfig;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 |
9 | /**
10 | * 通用阿里云日志服务appender配置
11 | *
12 | * @Author wanglaomo
13 | * @Date 2019/4/4
14 | **/
15 | public class LogAppenderConfig {
16 |
17 | private static final String DEFAULT_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n";
18 |
19 | private CommonProjectConfig projectConfig;
20 |
21 | private ProducerConfig producerConfig;
22 |
23 | private boolean enable = false;
24 |
25 | private String topic; //
26 |
27 | private String source; //
28 |
29 | private String timeZone = "UTC";
30 |
31 | private String charset = "UTF-8";
32 |
33 | private String timeFormat = "yyyy-MM-dd'T'HH:mmZ";
34 |
35 | private List loggerFilter = new ArrayList<>();
36 |
37 | private String pattern = DEFAULT_PATTERN;
38 |
39 | public boolean isEnable() {
40 | return enable;
41 | }
42 |
43 | public void setEnable(boolean enable) {
44 | this.enable = enable;
45 | }
46 |
47 | public String getTopic() {
48 | return topic;
49 | }
50 |
51 | public void setTopic(String topic) {
52 | this.topic = topic;
53 | }
54 |
55 | public String getTimeZone() {
56 | return timeZone;
57 | }
58 |
59 | public void setTimeZone(String timeZone) {
60 | this.timeZone = timeZone;
61 | }
62 |
63 | public String getTimeFormat() {
64 | return timeFormat;
65 | }
66 |
67 | public void setTimeFormat(String timeFormat) {
68 | this.timeFormat = timeFormat;
69 | }
70 |
71 | public CommonProjectConfig getProjectConfig() {
72 | return projectConfig;
73 | }
74 |
75 | public void setProjectConfig(CommonProjectConfig projectConfig) {
76 | this.projectConfig = projectConfig;
77 | }
78 |
79 | public ProducerConfig getProducerConfig() {
80 | return producerConfig;
81 | }
82 |
83 | public void setProducerConfig(ProducerConfig producerConfig) {
84 | this.producerConfig = producerConfig;
85 | }
86 |
87 | public List getLoggerFilter() {
88 | return loggerFilter;
89 | }
90 |
91 | public void setLoggerFilter(List loggerFilter) {
92 | this.loggerFilter = loggerFilter;
93 | }
94 |
95 | public String getPattern() {
96 | return pattern;
97 | }
98 |
99 | public void setPattern(String pattern) {
100 | this.pattern = pattern;
101 | }
102 |
103 | public String getSource() {
104 | return source;
105 | }
106 |
107 | public String getCharset() {
108 | return charset;
109 | }
110 |
111 | public void setCharset(String charset) {
112 | this.charset = charset;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/config/LogAppenderConfigLoader.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log.config;
2 |
3 | import org.springframework.core.env.ConfigurableEnvironment;
4 |
5 | import java.lang.reflect.InvocationTargetException;
6 |
7 | /**
8 | * Config loader
9 | *
10 | * @Author wanglaomo
11 | * @Date 2019/4/8
12 | **/
13 | public interface LogAppenderConfigLoader {
14 |
15 | // 从spring environment获取配置
16 | LogAppenderConfig load(ConfigurableEnvironment environment) throws InvocationTargetException, IllegalAccessException;
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/enhancer/AbstractLogEnhancer.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log.enhancer;
2 |
3 | import cn.keking.common.log.config.LogAppenderConfig;
4 |
5 | /**
6 | * Abstract log enhancer
7 | *
8 | * @Author wanglaomo
9 | * @Date 2019/4/4
10 | **/
11 | public abstract class AbstractLogEnhancer implements LogEnhancer {
12 |
13 | @Override
14 | public void enhance(LogAppenderConfig config) {
15 |
16 | try {
17 | if(!hasBeanEnhanced()) {
18 | doEnhance(config);
19 | }
20 | } catch (Exception e) {
21 | handlerEnhanceError(e);
22 | } finally {
23 | afterEnhance();
24 | }
25 | }
26 |
27 | protected abstract boolean hasBeanEnhanced();
28 |
29 | protected abstract void doEnhance(LogAppenderConfig config);
30 |
31 | protected abstract void afterEnhance();
32 |
33 | protected void handlerEnhanceError(Exception exception) {
34 |
35 | cleanUp();
36 | System.err.println("Failed to add aliyun appender");
37 | exception.printStackTrace(System.err);
38 | throw new IllegalStateException(exception);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/enhancer/DefaultLog4j2Enhancer.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log.enhancer;
2 |
3 | import cn.keking.common.log.Constants;
4 | import cn.keking.common.log.appender.log4j2.Log4j2AliYunAppender;
5 | import cn.keking.common.log.config.LogAppenderConfig;
6 | import cn.keking.common.log.utils.LogEnvUtils;
7 | import org.apache.commons.lang.StringUtils;
8 | import org.apache.logging.log4j.LogManager;
9 | import org.apache.logging.log4j.core.Appender;
10 | import org.apache.logging.log4j.core.LoggerContext;
11 | import org.apache.logging.log4j.core.config.Configuration;
12 | import org.apache.logging.log4j.core.config.LoggerConfig;
13 | import org.apache.logging.log4j.core.layout.PatternLayout;
14 | import org.springframework.util.ClassUtils;
15 |
16 | import java.nio.charset.Charset;
17 | import java.util.List;
18 | import java.util.Map;
19 |
20 |
21 | /**
22 | * Default log enhancer for log4j2
23 | *
24 | * @Author wanglaomo
25 | * @Date 2019/4/3
26 | **/
27 | public class DefaultLog4j2Enhancer extends AbstractLogEnhancer{
28 |
29 | private final ClassLoader classLoader;
30 |
31 | private LoggerContext ctx;
32 |
33 | public DefaultLog4j2Enhancer(ClassLoader classLoader) {
34 |
35 | this.classLoader = classLoader;
36 | ctx = getLoggerContext();
37 | }
38 |
39 | @Override
40 | public boolean alreadyBound() {
41 |
42 | Class clazz = LogEnvUtils.loadAliyunLog4j2AppenderExist(classLoader);
43 | if(clazz == null) {
44 | return false;
45 | } else {
46 |
47 | Map appenderMap = ctx.getConfiguration().getAppenders();
48 | for (Map.Entry entry : appenderMap.entrySet()) {
49 | String name = entry.getKey();
50 | Appender appender = entry.getValue();
51 | if (appender.getClass().isAssignableFrom(clazz)) {
52 | return true;
53 | }
54 | }
55 | return false;
56 | }
57 |
58 |
59 | }
60 |
61 | @Override
62 | public void cleanUp() {
63 |
64 | markAsUnEnhanced();
65 | }
66 |
67 | protected void doEnhance(LogAppenderConfig logAppenderConfig) {
68 |
69 | final Configuration config = ctx.getConfiguration();
70 |
71 | // config pattern
72 | Appender appender = null;
73 | String pattern = logAppenderConfig.getPattern();
74 | if(!StringUtils.isBlank(pattern)) {
75 | PatternLayout layout = PatternLayout
76 | .newBuilder()
77 | .withPattern(pattern)
78 | .withCharset(Charset.forName(logAppenderConfig.getCharset()))
79 | .build();
80 | appender = new Log4j2AliYunAppender(logAppenderConfig, layout);
81 | } else {
82 | appender = new Log4j2AliYunAppender(logAppenderConfig);
83 | }
84 |
85 | appender.start();
86 | config.addAppender(appender);
87 | List loggerFilter = logAppenderConfig.getLoggerFilter();
88 | // log4j2 root logger name is ""
89 | if(loggerFilter.remove(Constants.DEFAULT_ROOT_LOGGER_NAME)) {
90 | loggerFilter.add("");
91 | }
92 |
93 | final Map loggerMap = config.getLoggers();
94 | Appender finalAppender = appender;
95 | loggerMap.forEach((loggerName, loggerConfig) -> {
96 | if(!loggerFilter.contains(loggerName)) {
97 | loggerConfig.addAppender(finalAppender, null, null);
98 | }
99 | });
100 |
101 | ctx.updateLoggers();
102 | }
103 |
104 | protected void afterEnhance() {
105 | markAsEnhanced();
106 | }
107 |
108 | protected boolean hasBeanEnhanced() {
109 |
110 | if(ctx.getConfiguration().getProperties()
111 | .get(DefaultLog4j2Enhancer.class.getCanonicalName()) != null) {
112 |
113 | return true;
114 | }
115 |
116 | return false;
117 | }
118 |
119 | private void markAsEnhanced() {
120 |
121 | ctx.getConfiguration().getProperties()
122 | .put(DefaultLog4j2Enhancer.class.getCanonicalName(), "");
123 | }
124 |
125 | private void markAsUnEnhanced() {
126 |
127 | ctx.getConfiguration().getProperties()
128 | .remove(DefaultLog4j2Enhancer.class.getCanonicalName());
129 | }
130 |
131 | private LoggerContext getLoggerContext() {
132 |
133 | try {
134 | ClassUtils.forName(LogManager.class.getName(), classLoader);
135 | } catch (ClassNotFoundException e) {
136 | throw new IllegalStateException("Cannot find LogManager, but Log4j2 is on the classpath");
137 | }
138 |
139 | return (LoggerContext) LogManager.getContext(false);
140 | }
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/enhancer/DefaultLogbackEnhancer.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log.enhancer;
2 |
3 | import ch.qos.logback.classic.Level;
4 | import ch.qos.logback.classic.Logger;
5 | import ch.qos.logback.classic.LoggerContext;
6 | import ch.qos.logback.classic.spi.ILoggingEvent;
7 | import ch.qos.logback.classic.turbo.TurboFilter;
8 | import ch.qos.logback.core.Appender;
9 | import ch.qos.logback.core.spi.FilterReply;
10 | import cn.keking.common.log.appender.logback.LogbackAliYunAppender;
11 | import cn.keking.common.log.config.LogAppenderConfig;
12 | import cn.keking.common.log.utils.LogEnvUtils;
13 | import org.apache.commons.lang.StringUtils;
14 | import org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout;
15 | import org.slf4j.ILoggerFactory;
16 | import org.slf4j.Marker;
17 | import org.slf4j.impl.StaticLoggerBinder;
18 |
19 | import java.nio.charset.Charset;
20 | import java.util.Iterator;
21 | import java.util.List;
22 |
23 | /**
24 | * Default log enhancer for logback
25 | *
26 | * @Author wanglaomo
27 | * @Date 2019/4/3
28 | **/
29 | public class DefaultLogbackEnhancer extends AbstractLogEnhancer {
30 |
31 | private final ClassLoader classLoader;
32 |
33 | private static LoggerContext ctx;
34 |
35 | public DefaultLogbackEnhancer(ClassLoader classLoader) {
36 | this.classLoader = classLoader;
37 | ctx = getLoggerContext();
38 | }
39 |
40 | @Override
41 | public boolean alreadyBound() {
42 |
43 | Class clazz = LogEnvUtils.loadAliyunLogbackAppenderExist(classLoader);
44 | if(clazz == null) {
45 | return false;
46 | } else {
47 | List loggerList = ctx.getLoggerList();
48 | for(Logger logger : loggerList) {
49 | Iterator> iterator = logger.iteratorForAppenders();
50 | while(iterator.hasNext()) {
51 | Appender appender = iterator.next();
52 | if (appender.getClass().isAssignableFrom(clazz)) {
53 | return true;
54 | }
55 | }
56 | }
57 | return false;
58 | }
59 |
60 | }
61 |
62 | @Override
63 | public void cleanUp() {
64 |
65 | markAsUnEnhanced();
66 | }
67 |
68 | @Override
69 | protected void doEnhance(LogAppenderConfig config) {
70 |
71 | LogbackAliYunAppender aliYunAppender = new LogbackAliYunAppender(config);
72 | aliYunAppender.setContext(ctx);
73 |
74 | // config pattern
75 | if(!StringUtils.isBlank(config.getPattern())) {
76 | PatternLayoutEncoder encoder = new PatternLayoutEncoder();
77 | encoder.setPattern(config.getPattern());
78 | encoder.setContext(ctx);
79 | encoder.setPatternLayout(new TraceIdPatternLogbackLayout());
80 | encoder.start();
81 | encoder.setCharset(Charset.forName(config.getCharset()));
82 | aliYunAppender.setEncoder(encoder);
83 | }
84 |
85 | aliYunAppender.start();
86 | List loggerFilter = config.getLoggerFilter();
87 |
88 | ctx.addTurboFilter(new TurboFilter() {
89 |
90 | @Override
91 | public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) {
92 |
93 | if (!logger.isAttached(aliYunAppender) && !loggerFilter.contains(logger.getName())) {
94 |
95 | logger.addAppender(aliYunAppender);
96 | }
97 | return FilterReply.NEUTRAL;
98 | }
99 | });
100 | }
101 |
102 |
103 | protected void afterEnhance() {
104 | markAsEnhanced();
105 | }
106 |
107 | protected boolean hasBeanEnhanced() {
108 | return ctx.getObject(DefaultLogbackEnhancer.class.getCanonicalName()) != null;
109 | }
110 |
111 | private void markAsEnhanced() {
112 |
113 | ctx.putObject(DefaultLogbackEnhancer.class.getCanonicalName(), new Object());
114 | }
115 |
116 | private void markAsUnEnhanced() {
117 |
118 | ctx.removeObject(DefaultLogbackEnhancer.class.getCanonicalName());
119 | }
120 |
121 | private LoggerContext getLoggerContext() {
122 | ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
123 |
124 | if(factory instanceof LoggerContext) {
125 |
126 | return (LoggerContext) factory;
127 | }
128 | throw new IllegalStateException("ILoggerFactory is not a Logback LoggerContext, but Logback is on the classpath.");
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/enhancer/LogEnhancer.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log.enhancer;
2 |
3 | import cn.keking.common.log.config.LogAppenderConfig;
4 |
5 | /**
6 | * @Author wanglaomo
7 | * @Date 2019/4/3
8 | **/
9 | public interface LogEnhancer {
10 |
11 | // 判断是否已经绑定阿里云SDK
12 | boolean alreadyBound();
13 |
14 | // 接入阿里云日志服务
15 | void enhance(LogAppenderConfig config);
16 |
17 | void cleanUp();
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/enhancer/PatternLayoutEncoder.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log.enhancer;
2 |
3 | import ch.qos.logback.classic.PatternLayout;
4 | import ch.qos.logback.classic.spi.ILoggingEvent;
5 | import ch.qos.logback.core.pattern.PatternLayoutEncoderBase;
6 |
7 | /**
8 | * @Author wanglaomo
9 | * @Date 2019/4/12
10 | **/
11 | public class PatternLayoutEncoder extends PatternLayoutEncoderBase {
12 |
13 | private PatternLayout patternLayout;
14 |
15 | @Override
16 | public void start() {
17 | patternLayout.setContext(context);
18 | patternLayout.setPattern(getPattern());
19 | patternLayout.setOutputPatternAsHeader(outputPatternAsHeader);
20 | patternLayout.start();
21 | this.layout = patternLayout;
22 | super.start();
23 | }
24 |
25 | public void setPatternLayout(PatternLayout layout) {
26 |
27 | this.patternLayout = layout;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/cn/keking/common/log/utils/LogEnvUtils.java:
--------------------------------------------------------------------------------
1 | package cn.keking.common.log.utils;
2 |
3 | public final class LogEnvUtils {
4 |
5 | private LogEnvUtils() {
6 | }
7 |
8 | public static boolean isLogbackUsable(ClassLoader classloader) {
9 |
10 | try {
11 | return classloader.loadClass("ch.qos.logback.classic.LoggerContext") != null;
12 | } catch (ClassNotFoundException e) {
13 | return false;
14 | }
15 | }
16 |
17 | public static boolean isLog4j2Usable(ClassLoader classloader) {
18 |
19 | try {
20 | return (classloader.loadClass("org.apache.logging.slf4j.Log4jLoggerFactory") != null);
21 | } catch (ClassNotFoundException e) {
22 | return false;
23 | }
24 | }
25 |
26 | public static Class loadAliyunLog4j2AppenderExist(ClassLoader classloader) {
27 |
28 | try {
29 | return classloader.loadClass("com.aliyun.openservices.log.log4j2.LoghubAppender");
30 | } catch (ClassNotFoundException e) {
31 | return null;
32 | }
33 | }
34 |
35 | public static Class loadAliyunLogbackAppenderExist(ClassLoader classloader) {
36 |
37 | try {
38 | return classloader.loadClass("com.aliyun.openservices.log.logback.LoghubAppender");
39 | } catch (ClassNotFoundException e) {
40 | return null;
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2 | cn.keking.common.log.LogAppenderAutoConfiguration
--------------------------------------------------------------------------------
/src/main/resources/aliyunlog.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kekingcn/aliyunlog-spring-boot-starter/a4c5e859d680d8d2b1d6384ebc93d0bd5bcec862/src/main/resources/aliyunlog.properties
--------------------------------------------------------------------------------