├── .github
├── renovate.json
└── workflows
│ ├── github-release.yml
│ ├── maven.yml
│ ├── oss-release-deploy.yml
│ └── oss-snapshot-deploy.yml
├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
└── main
├── java
└── com
│ └── pig4cloud
│ └── trace
│ ├── Constants.java
│ ├── TraceAutoConfiguration.java
│ ├── TraceContentFactory.java
│ ├── TraceFormatEnum.java
│ ├── TraceLogProperties.java
│ ├── handlers
│ ├── DefaultTraceMetaObjectHandler.java
│ └── TraceMetaObjectHandler.java
│ ├── instrument
│ ├── feign
│ │ ├── TraceFeignClientAutoConfiguration.java
│ │ └── TraceFeignRequestInterceptor.java
│ ├── gateway
│ │ ├── TraceGatewayAutoConfiguration.java
│ │ └── TraceGatewayFilter.java
│ ├── reactive
│ │ ├── TraceReactiveConfiguration.java
│ │ └── TraceReactiveFilter.java
│ ├── resttemplate
│ │ ├── RestTemplatePostProcessor.java
│ │ ├── TraceClientHttpRequestInterceptor.java
│ │ └── TraceRestTemplateConfiguration.java
│ └── servlet
│ │ ├── TraceServletConfiguration.java
│ │ └── TraceServletFilter.java
│ ├── metadata
│ ├── TraceFormatInfo.java
│ └── TraceFormatInfoHelper.java
│ ├── processor
│ └── TraceEnvironmentPostProcessor.java
│ └── util
│ ├── EnvironmentUtils.java
│ ├── TraceIdUtil.java
│ └── URLUtil.java
└── resources
└── META-INF
├── spring.factories
└── spring
└── org.springframework.boot.autoconfigure.AutoConfiguration.imports
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "baseBranches": ["jdk17-dev", "boot-dev"],
4 | "extends": [
5 | "config:recommended"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.github/workflows/github-release.yml:
--------------------------------------------------------------------------------
1 | name: publish github release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | releaseversion:
7 | description: 'Release version'
8 | required: true
9 | default: '3.7.0'
10 |
11 | jobs:
12 | publish-github-release:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | - name: Generate changelog
17 | id: changelog
18 | uses: metcalfc/changelog-generator@v4.3.1
19 | with:
20 | myToken: ${{ secrets.GH_TOKEN }}
21 |
22 | - name: Create GitHub Release
23 | id: create_release
24 | uses: actions/create-release@v1
25 | env:
26 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
27 | with:
28 | tag_name: ${{ github.event.inputs.releaseversion }}
29 | release_name: ${{ github.event.inputs.releaseversion }}
30 | body: |
31 | ### Things that changed in this release
32 | ${{ steps.changelog.outputs.changelog }}
33 | draft: false
34 | prerelease: ${{ contains(github.event.inputs.releaseversion, '-') }}
35 |
--------------------------------------------------------------------------------
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Maven
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
3 |
4 | name: oss-spring-boot-starter
5 |
6 | on:
7 | push:
8 | branches: [ master,dev ]
9 | pull_request:
10 | branches: [ master,dev ]
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v2
17 | - name: Set up JDK 17
18 | uses: actions/setup-java@v2
19 | with:
20 | java-version: '17'
21 | distribution: 'adopt'
22 |
23 | - name: mvn clean install
24 | run: mvn clean install
25 |
26 | - name: mvn spring-javaformat:validate
27 | run: mvn spring-javaformat:validate
28 |
--------------------------------------------------------------------------------
/.github/workflows/oss-release-deploy.yml:
--------------------------------------------------------------------------------
1 | name: publish maven package
2 | on:
3 | workflow_dispatch:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | jobs:
9 | oss-release-deploy:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v3
14 | - name: Set up JDK 17
15 | uses: actions/setup-java@v2
16 | with:
17 | java-version: '17'
18 | distribution: 'adopt'
19 | cache: maven
20 |
21 | - name: Setup Maven Central
22 | uses: actions/setup-java@v3
23 | with: # overwrite settings.xml
24 | java-version: '17'
25 | distribution: 'adopt'
26 | server-id: sonatype
27 | server-username: OSSRH_USERNAME
28 | server-password: OSSRH_PASSWORD
29 | gpg-private-key: ${{ secrets.MAVEN_GPG_KEY }}
30 | gpg-passphrase: MAVEN_GPG_PASSPHRASE
31 |
32 | - name: Publish to Maven Central
33 | run: mvn clean deploy -P release -Dmaven.test.skip=true
34 | env:
35 | MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
36 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
37 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
38 |
--------------------------------------------------------------------------------
/.github/workflows/oss-snapshot-deploy.yml:
--------------------------------------------------------------------------------
1 | name: publish maven package
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches: [ master ]
6 |
7 | jobs:
8 | oss-snapshot-deploy:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@v3
13 | - name: Set up JDK 17
14 | uses: actions/setup-java@v2
15 | with:
16 | java-version: '17'
17 | distribution: 'adopt'
18 | cache: maven
19 |
20 | - name: Setup Maven Central
21 | uses: actions/setup-java@v3
22 | with: # overwrite settings.xml
23 | java-version: '17'
24 | distribution: 'adopt'
25 | server-id: sonatype
26 | server-username: OSSRH_USERNAME
27 | server-password: OSSRH_PASSWORD
28 | gpg-private-key: ${{ secrets.MAVEN_GPG_KEY }}
29 | gpg-passphrase: MAVEN_GPG_PASSPHRASE
30 |
31 | - name: Set Release version env variable
32 | run: |
33 | echo "RELEASE_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV
34 |
35 | - name: Publish to Maven Central
36 | if: contains('${{ env.RELEASE_VERSION }}', 'SNAPSHOT')
37 | run: mvn clean deploy -P snapshot -Dmaven.test.skip=true
38 | env:
39 | MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
40 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
41 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
42 |
--------------------------------------------------------------------------------
/.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 |
30 | ### VS Code ###
31 | .vscode/
32 |
--------------------------------------------------------------------------------
/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 | # SpringBoot之微服务日志链路追踪
2 |
3 | ## 简介
4 |
5 | 在微服务里,业务出现问题或者程序出的任何问题,都少不了查看日志,一般我们使用 `ELK`
6 | 相关的日志收集工具,服务多的情况下,业务问题也是有些难以排查,只能确定大致时间定位相关日志。`log-trace-spring-boot-starter` 解决多个服务调用日志的问题,它可以将一个完整的调用链给整合为一个完整有序的日志。
7 |
8 | 支持组件:
9 |
10 | - spring cloud gateway 调用
11 | - feign 调用
12 | - restTemplate 调用
13 |
14 | 日志输出格式:
15 |
16 | ```
17 | 2022-04-09 22:16:05.796 INFO [log-trace-service-a-demo,ac8ffaaed5f343da,log-trace-gateway-demo,,] 88948 --- [nio-8081-exec-7] c.p.l.t.service.a.demo.TestController : controller test2 执行 ac8ffaaed5f343da
18 | 2022-04-09 22:16:05.569 INFO [log-trace-service-a-demo,04cf5392dc5c4881,log-trace-gateway-demo,,] 88948 --- [nio-8081-exec-9] c.p.l.t.service.a.demo.TestController : controller test2 执行 04cf5392dc5c4881
19 | 2022-04-09 22:16:05.183 INFO [log-trace-service-a-demo,86b5c555ce4f4451,log-trace-gateway-demo,,] 88948 --- [nio-8081-exec-1] c.p.l.t.service.a.demo.TestController : controller test2 执行 86b5c555ce4f4451
20 | ```
21 |
22 | 我们可以通过 `86b5c555ce4f4451` id 进行查询链路上的所有日志信息。
23 |
24 | `log-trace-service-a-demo` 为当前应用。
25 | `log-trace-gateway-demo` 为上游应用。
26 |
27 | 当然这些参数可以基于业务定制的。
28 |
29 | ## 功能使用
30 |
31 | ### 添加依赖
32 |
33 | ```
34 |
35 | com.pig4cloud.plugin
36 | log-trace-spring-boot3-starter
37 | 3.2.0
38 |
39 | ```
40 |
41 | ### 配置应用
42 |
43 |
44 |
45 | 这里以3个微服务来举例子。
46 |
47 | 1. `log-trace-gateway-demo` 充当网关功能
48 | 2. `log-trace-service-a-demo` 充当服务A
49 | 3. `log-trace-service-b-demo` 充当服务B
50 |
51 | **调用链路为:**
52 |
53 | `log-trace-gateway-demo` -> `log-trace-service-a-demo` `TestController#test`
54 | -> `log-trace-service-b-demo` `TestController#test`
55 |
56 | 访问网关地址: `http://127.0.0.1:8000/a/test`
57 |
58 | **网关日志如下:**
59 |
60 | ```
61 | 2022-04-09 22:16:05.434 DEBUG [33b07a9c5f324375,this] 89996 --- [nio-8000-exec-1] c.p.l.t.s.i.gateway.TracePregatewayFilter : gateway traceid 33b07a9c5f324375
62 | ```
63 |
64 | 网关转发至服务A
65 |
66 | **服务A 日志如下:**
67 |
68 | ```
69 | 2022-04-09 22:16:05.476 INFO [log-trace-service-a-demo,33b07a9c5f324375,log-trace-gateway-demo,,] 88948 --- [nio-8081-exec-5] c.p.l.t.service.a.demo.TestController : controller test2 执行 33b07a9c5f324375
70 | ```
71 |
72 | 服务A 调用 服务B
73 |
74 | **服务B 日志如下:**
75 |
76 | ```
77 | 2022-04-09 22:16:05.478 INFO [log-trace-service-b-demo,33b07a9c5f324375,log-trace-service-a-demo,,] 88952 --- [nio-8082-exec-3] c.p.l.t.servcie.b.demo.TestController : header traceId 33b07a9c5f324375
78 | 2022-04-09 22:16:05.478 INFO [log-trace-service-b-demo,33b07a9c5f324375,log-trace-service-a-demo,,] 88952 --- [nio-8082-exec-3] c.p.l.t.servcie.b.demo.TestController : controller test 执行 33b07a9c5f324375
79 | 2022-04-09 22:16:05.478 INFO [log-trace-service-b-demo,33b07a9c5f324375,log-trace-service-a-demo,,] 88952 --- [nio-8082-exec-3] c.p.l.trace.servcie.b.demo.TestService : test 方法执行 33b07a9c5f324375
80 | 2022-04-09 22:16:05.478 INFO [log-trace-service-b-demo,33b07a9c5f324375,log-trace-service-a-demo,,] 88952 --- [nio-8082-exec-3] c.p.l.trace.servcie.b.demo.TestService : test1 方法执行 33b07a9c5f324375
81 | ```
82 |
83 | 这样可以在第三方日志平台按照一个id进行查询了。
84 |
85 | 如 `ELK` 通过 `33b07a9c5f324375` id 查询出相关的所有链路调用。
86 |
87 | ### 配置输出格式
88 |
89 | **目前支持以上参数:**
90 |
91 | ```
92 | X-B3-ParentName 上游服务名称
93 | X-B3-TraceId 为一个请求分配的ID号,用来标识一条请求链路。
94 | ```
95 |
96 | **通过 `application.properties` 进行配置。**
97 |
98 | ```
99 | spring.trace.log.format=X-B3-TraceId,X-B3-ParentName
100 |
101 |
102 | `spring.trace.log.format` 配置参数顺序将影响日志输出格式。
103 |
104 | 不配置将按照默认格式输出。
105 | ```
106 |
107 | 日志输出如下:
108 |
109 | ```
110 | 2022-04-09 22:15:57.434 DEBUG [33b07a9c5f324375,this] 89996 --- [nio-8000-exec-1] c.p.l.t.s.i.gateway.TracePregatewayFilter : gateway traceid 33b07a9c5f324375
111 | ```
112 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | com.pig4cloud.plugin
6 | log-trace-spring-boot3-starter
7 | 3.2.0
8 | jar
9 |
10 | log-trace-spring-boot-starter
11 | log-trace-spring-boot-starter
12 | 4.0.0
13 | https://gitee.com/log4j/log-trace-spring-boott
14 |
15 |
16 |
17 |
18 | The Apache License, Version 2.0
19 | http://www.apache.org/licenses/LICENSE-2.0.txt
20 | repo
21 | A business-friendly OSS license
22 |
23 |
24 |
25 |
26 |
27 |
28 | purgeyao
29 | 16621377702@163.com
30 |
31 | owner
32 |
33 | +8
34 |
35 |
36 |
37 |
38 |
39 | scm:git:git://github.com/purgeteam/log-trace-spring-boot.git
40 | scm:git:ssh://github.com/purgeteam/log-trace-spring-boot.git
41 | https://github.com/purgeteam/log-trace-spring-boot
42 |
43 |
44 |
45 | 3.2.3
46 | 2023.0.0
47 | 2.2.10.RELEASE
48 | 3.0.5
49 | 0.0.23
50 | 2.2.5
51 |
52 | 17
53 | ${java.version}
54 | ${java.version}
55 |
56 |
57 |
58 |
59 | org.springframework.boot
60 | spring-boot
61 | true
62 |
63 |
64 | org.springframework
65 | spring-webmvc
66 | true
67 |
68 |
69 | org.springframework
70 | spring-webflux
71 | true
72 |
73 |
74 | org.slf4j
75 | slf4j-api
76 |
77 |
78 | org.apache.tomcat.embed
79 | tomcat-embed-core
80 | true
81 |
82 |
83 | org.springframework.cloud
84 | spring-cloud-starter-openfeign
85 | true
86 |
87 |
88 | org.springframework.cloud
89 | spring-cloud-starter-gateway
90 | true
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | org.springframework.boot
100 | spring-boot-dependencies
101 | ${spring-boot.version}
102 | pom
103 | import
104 |
105 |
106 |
107 | org.springframework.cloud
108 | spring-cloud-starter-parent
109 | ${spring-cloud.version}
110 | pom
111 | import
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | pl.project13.maven
122 | git-commit-id-plugin
123 | ${git.commit.plugin}
124 |
125 |
126 |
127 | io.spring.javaformat
128 | spring-javaformat-maven-plugin
129 | ${spring.checkstyle.plugin}
130 |
131 |
132 |
133 |
134 |
135 |
136 | snapshot
137 |
138 |
139 |
140 |
141 | org.apache.maven.plugins
142 | maven-source-plugin
143 | 2.2.1
144 |
145 |
146 | package
147 |
148 | jar-no-fork
149 |
150 |
151 |
152 |
153 |
154 |
155 | org.apache.maven.plugins
156 | maven-javadoc-plugin
157 | 3.3.2
158 |
159 | private
160 | true
161 | UTF-8
162 | UTF-8
163 | UTF-8
164 | none
165 | false
166 |
167 |
168 |
169 | package
170 |
171 | jar
172 |
173 |
174 |
175 |
176 |
177 |
178 | org.apache.maven.plugins
179 | maven-gpg-plugin
180 | 3.0.1
181 |
182 |
183 | sign-artifacts
184 | verify
185 |
186 | sign
187 |
188 |
189 |
190 |
191 |
192 | --pinentry-mode
193 | loopback
194 |
195 |
196 |
197 |
198 | org.sonatype.plugins
199 | nexus-staging-maven-plugin
200 | 1.6.13
201 | true
202 |
203 | sonatype
204 | https://oss.sonatype.org/
205 | true
206 |
207 |
208 |
209 |
210 |
211 |
212 | sonatype
213 |
214 | https://oss.sonatype.org/content/repositories/snapshots/
215 |
216 |
217 |
218 |
219 |
220 | release
221 |
222 |
223 |
224 |
225 | org.apache.maven.plugins
226 | maven-source-plugin
227 | 2.2.1
228 |
229 |
230 | package
231 |
232 | jar-no-fork
233 |
234 |
235 |
236 |
237 |
238 |
239 | org.apache.maven.plugins
240 | maven-javadoc-plugin
241 | 3.3.2
242 |
243 | private
244 | true
245 | UTF-8
246 | UTF-8
247 | UTF-8
248 | none
249 | false
250 |
251 |
252 |
253 | package
254 |
255 | jar
256 |
257 |
258 |
259 |
260 |
261 |
262 | org.apache.maven.plugins
263 | maven-gpg-plugin
264 | 3.0.1
265 |
266 |
267 | sign-artifacts
268 | verify
269 |
270 | sign
271 |
272 |
273 |
274 |
275 |
276 | --pinentry-mode
277 | loopback
278 |
279 |
280 |
281 |
282 | org.sonatype.plugins
283 | nexus-staging-maven-plugin
284 | 1.6.13
285 | true
286 |
287 | sonatype
288 | https://oss.sonatype.org/
289 | true
290 |
291 |
292 |
293 |
294 |
295 |
296 | sonatype
297 |
298 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
299 |
300 |
301 |
302 |
303 |
304 |
305 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/Constants.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace;
2 |
3 | /**
4 | * 共用字段
5 | *
6 | * @author purgeyao
7 | * @since 1.0.0
8 | */
9 | public class Constants {
10 |
11 | /**
12 | * 当前项目名称
13 | */
14 | public static final String LOCAL_NAME = "spring.application.name";
15 |
16 | /**
17 | * 布尔类型。表示是否要将该信息输出到类似Zipkin这样的聚合器进行收集和展示
18 | */
19 | public static final String LEGACY_EXPORTABLE_NAME = "X-Span-Export";
20 |
21 | /**
22 | * parent id 父请求id
23 | */
24 | public static final String LEGACY_PARENT_ID_NAME = "X-B3-ParentSpanId";
25 |
26 | /**
27 | * parent service name 父服务名称
28 | */
29 | public static final String LEGACY_PARENT_SERVICE_NAME = "X-B3-ParentName";
30 |
31 | /**
32 | * 为一个请求分配的ID号,用来标识一条请求链路。
33 | */
34 | public static final String LEGACY_TRACE_ID_NAME = "X-B3-TraceId";
35 |
36 | /**
37 | * 表示一个基本的工作单元,一个请求可以包含多个步骤,每个步骤都拥有自己的spanId。 一个请求包含一个TraceId,多个SpanId
38 | */
39 | public static final String LEGACY_SPAN_ID_NAME = "X-B3-SpanId";
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/TraceAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace;
2 |
3 | import com.pig4cloud.trace.handlers.DefaultTraceMetaObjectHandler;
4 | import com.pig4cloud.trace.handlers.TraceMetaObjectHandler;
5 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | import java.util.Map;
10 |
11 | /**
12 | * @author purgeyao
13 | * @since 1.0
14 | */
15 | @Configuration(proxyBeanMethods = false)
16 | @EnableConfigurationProperties(TraceLogProperties.class)
17 | public class TraceAutoConfiguration {
18 |
19 | @Bean
20 | public TraceContentFactory traceContentFactory(Map traceMetaObjectHandlerMap) {
21 | return new TraceContentFactory(traceMetaObjectHandlerMap);
22 | }
23 |
24 | @Bean
25 | public DefaultTraceMetaObjectHandler defaultTraceMetaObjectHandler() {
26 | return new DefaultTraceMetaObjectHandler();
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/TraceContentFactory.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace;
2 |
3 | import com.pig4cloud.trace.handlers.TraceMetaObjectHandler;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.slf4j.MDC;
7 | import org.springframework.context.EnvironmentAware;
8 | import org.springframework.core.env.Environment;
9 |
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | /**
14 | * 请求链路内容工厂
15 | *
16 | * @author purgeyao
17 | * @since 1.0.0
18 | */
19 | public class TraceContentFactory implements EnvironmentAware {
20 |
21 | private static final Logger log = LoggerFactory.getLogger(TraceContentFactory.class);
22 |
23 | private static Environment environment;
24 |
25 | private static Map traceMetaObjectHandlerMap;
26 |
27 | public TraceContentFactory(Map traceMetaObjectHandlerMap) {
28 | TraceContentFactory.traceMetaObjectHandlerMap = traceMetaObjectHandlerMap;
29 | }
30 |
31 | /**
32 | * 储存本地 MDC
33 | * @param traceContentMap 内容集合
34 | */
35 | public static void storageMDC(Map traceContentMap) {
36 | // 执行 TraceMetaObjectHandler 拓展器
37 | for (Map.Entry entry : traceMetaObjectHandlerMap.entrySet()) {
38 | TraceMetaObjectHandler handler = entry.getValue();
39 | handler.additionalFill(traceContentMap);
40 | }
41 |
42 | // 写入 MDC
43 | for (Map.Entry entry : traceContentMap.entrySet()) {
44 | MDC.put(entry.getKey(), entry.getValue());
45 | }
46 |
47 | log.debug("[TraceContentFactory] 请求流量: traceId={}, ParentName={}", MDC.get(Constants.LEGACY_TRACE_ID_NAME),
48 | MDC.get(Constants.LEGACY_PARENT_SERVICE_NAME));
49 | }
50 |
51 | public Map assemblyTraceContent() {
52 | return buildTraceContent();
53 | }
54 |
55 | public static Map assemblyTraceContentStatic() {
56 | return buildTraceContent();
57 | }
58 |
59 | /**
60 | * 获取 MDC 内容 同时添加 X-B3-ParentName 参数
61 | * @return MDC map
62 | */
63 | private static Map buildTraceContent() {
64 | Map traceContentMap = MDC.getCopyOfContextMap();
65 | if (traceContentMap == null) {
66 | traceContentMap = new HashMap<>(16);
67 | }
68 | String serviceName = environment.getProperty("spring.application.name");
69 | traceContentMap.put(Constants.LEGACY_PARENT_SERVICE_NAME, serviceName);
70 | return traceContentMap;
71 | }
72 |
73 | @Override
74 | public void setEnvironment(Environment environment) {
75 | TraceContentFactory.environment = environment;
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/TraceFormatEnum.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace;
2 |
3 | /**
4 | * trace 格式 枚举
5 | *
6 | * @author purgeyao
7 | * @since 1.0.0
8 | */
9 | public enum TraceFormatEnum {
10 |
11 | /**
12 | * 当前项目名称
13 | */
14 | LOCAL_NAME {
15 | @Override
16 | public String getValue() {
17 | return Constants.LOCAL_NAME;
18 | }
19 | },
20 |
21 | /**
22 | * 布尔类型。表示是否要将该信息输出到类似Zipkin这样的聚合器进行收集和展示
23 | */
24 | EXPORT {
25 | @Override
26 | public String getValue() {
27 | return Constants.LEGACY_EXPORTABLE_NAME;
28 | }
29 | },
30 |
31 | /**
32 | * parent id 父请求id
33 | */
34 | PARENT_SPAN_ID {
35 | @Override
36 | public String getValue() {
37 | return Constants.LEGACY_PARENT_ID_NAME;
38 | }
39 | },
40 |
41 | /**
42 | * parent service name 父服务名称
43 | */
44 | PARENT_NAME {
45 | @Override
46 | public String getValue() {
47 | return Constants.LEGACY_PARENT_SERVICE_NAME;
48 | }
49 | },
50 |
51 | /**
52 | * 为一个请求分配的ID号,用来标识一条请求链路。
53 | */
54 | TRACE_ID {
55 | @Override
56 | public String getValue() {
57 | return Constants.LEGACY_TRACE_ID_NAME;
58 | }
59 | },
60 |
61 | /**
62 | * 表示一个基本的工作单元,一个请求可以包含多个步骤,每个步骤都拥有自己的spanId。 一个请求包含一个TraceId,多个SpanId
63 | */
64 | SPAN_ID {
65 | @Override
66 | public String getValue() {
67 | return Constants.LEGACY_SPAN_ID_NAME;
68 | }
69 | };
70 |
71 | /**
72 | * 获取参数名称
73 | */
74 | public abstract String getValue();
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/TraceLogProperties.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace;
2 |
3 | import org.springframework.beans.factory.InitializingBean;
4 | import org.springframework.boot.context.properties.ConfigurationProperties;
5 |
6 | import java.util.HashSet;
7 | import java.util.Set;
8 |
9 | /**
10 | * 链路追踪配置
11 | *
12 | * @author purgeyao
13 | * @since 1.0.0
14 | */
15 | @ConfigurationProperties(prefix = "spring.trace.log")
16 | public class TraceLogProperties implements InitializingBean {
17 |
18 | /**
19 | * 日志格式顺序
20 | */
21 | private Set format = new HashSet<>();
22 |
23 | public Set getFormat() {
24 | return format;
25 | }
26 |
27 | public void setFormat(Set format) {
28 | this.format = format;
29 | }
30 |
31 | /**
32 | * X-B3-TraceId,X-B3-ParentName
33 | * @throws Exception
34 | */
35 | @Override
36 | public void afterPropertiesSet() throws Exception {
37 | if (0 == format.size()) {
38 | format.add(Constants.LEGACY_TRACE_ID_NAME);
39 | format.add(Constants.LEGACY_PARENT_SERVICE_NAME);
40 | }
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/handlers/DefaultTraceMetaObjectHandler.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.handlers;
2 |
3 | import com.pig4cloud.trace.Constants;
4 | import com.pig4cloud.trace.util.TraceIdUtil;
5 | import org.springframework.util.StringUtils;
6 |
7 | import java.util.Map;
8 |
9 | /**
10 | * 默认日志格式处理器
11 | *
12 | * @author purgeyao
13 | * @since 1.0.0
14 | */
15 | public class DefaultTraceMetaObjectHandler implements TraceMetaObjectHandler {
16 |
17 | @Override
18 | public void additionalFill(Map traceContentMap) {
19 | String headerTraceId = traceContentMap.get(Constants.LEGACY_TRACE_ID_NAME);
20 | // 如果为空,则表示第一次访问,即上游服务端的请求
21 | if (StringUtils.isEmpty(headerTraceId)) {
22 | this.strictInsertFill(traceContentMap, Constants.LEGACY_TRACE_ID_NAME, TraceIdUtil.traceIdString());
23 | }
24 | else {
25 | this.strictInsertFill(traceContentMap, Constants.LEGACY_TRACE_ID_NAME, headerTraceId);
26 | }
27 | // "ParentName" 如果为空设置为 this
28 | String headerParentName = traceContentMap.get(Constants.LEGACY_PARENT_SERVICE_NAME);
29 | if (StringUtils.isEmpty(headerParentName)) {
30 | this.strictInsertFill(traceContentMap, Constants.LEGACY_PARENT_SERVICE_NAME, "this");
31 | }
32 | else {
33 | this.strictInsertFill(traceContentMap, Constants.LEGACY_PARENT_SERVICE_NAME, headerParentName);
34 | }
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/handlers/TraceMetaObjectHandler.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.handlers;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * 元对象字段填充控制器抽象类,实现自定义日志参数写入
7 | *
8 | * @author purgeyao
9 | * @since 1.0.0
10 | */
11 | public interface TraceMetaObjectHandler {
12 |
13 | /**
14 | * 额外参数填充
15 | */
16 | void additionalFill(Map traceContentMap);
17 |
18 | /**
19 | * 根据 日志格式名称 填充 内容
20 | * @param traceContent trace日志格式内容
21 | * @param fieldName 日志格式名称参数
22 | * @param fieldVal 内容
23 | */
24 | default void strictInsertFill(Map traceContent, String fieldName, String fieldVal) {
25 | traceContent.put(fieldName, fieldVal);
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/instrument/feign/TraceFeignClientAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.instrument.feign;
2 |
3 | import com.pig4cloud.trace.TraceContentFactory;
4 | import feign.Client;
5 | import org.springframework.boot.autoconfigure.AutoConfigureBefore;
6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
7 | import org.springframework.cloud.openfeign.FeignAutoConfiguration;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.context.annotation.Configuration;
10 |
11 | /**
12 | * @author purgeyao
13 | * @since 1.0
14 | */
15 | @Configuration(proxyBeanMethods = false)
16 | @ConditionalOnClass({ Client.class, FeignAutoConfiguration.class, TraceContentFactory.class })
17 | @AutoConfigureBefore(FeignAutoConfiguration.class)
18 | public class TraceFeignClientAutoConfiguration {
19 |
20 | @Bean
21 | public TraceFeignRequestInterceptor basicAuthRequestInterceptor(TraceContentFactory traceContentFactory) {
22 | return new TraceFeignRequestInterceptor(traceContentFactory);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/instrument/feign/TraceFeignRequestInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.instrument.feign;
2 |
3 | import com.pig4cloud.trace.TraceContentFactory;
4 | import feign.RequestInterceptor;
5 | import feign.RequestTemplate;
6 |
7 | import java.util.Map;
8 |
9 | /**
10 | * @author purgeyao
11 | * @since 1.0
12 | */
13 | public class TraceFeignRequestInterceptor implements RequestInterceptor {
14 |
15 | private final TraceContentFactory traceContentFactory;
16 |
17 | public TraceFeignRequestInterceptor(TraceContentFactory traceContentFactory) {
18 | this.traceContentFactory = traceContentFactory;
19 | }
20 |
21 | @Override
22 | public void apply(RequestTemplate requestTemplate) {
23 | Map copyOfContextMap = traceContentFactory.assemblyTraceContent();
24 | for (Map.Entry copyOfContext : copyOfContextMap.entrySet()) {
25 | requestTemplate.header(copyOfContext.getKey(), copyOfContext.getValue());
26 | }
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/instrument/gateway/TraceGatewayAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.instrument.gateway;
2 |
3 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
5 | import org.springframework.cloud.gateway.filter.GlobalFilter;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | /**
10 | * 网关
11 | *
12 | * @author L.cm
13 | */
14 | @Configuration(proxyBeanMethods = false)
15 | @ConditionalOnClass(GlobalFilter.class)
16 | @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
17 | public class TraceGatewayAutoConfiguration {
18 |
19 | @Bean
20 | public TraceGatewayFilter traceGatewayFilter() {
21 | return new TraceGatewayFilter();
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/instrument/gateway/TraceGatewayFilter.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.instrument.gateway;
2 |
3 | import com.pig4cloud.trace.Constants;
4 | import com.pig4cloud.trace.TraceContentFactory;
5 | import org.slf4j.MDC;
6 | import org.springframework.cloud.gateway.filter.GatewayFilterChain;
7 | import org.springframework.cloud.gateway.filter.GlobalFilter;
8 | import org.springframework.core.Ordered;
9 | import org.springframework.http.HttpHeaders;
10 | import org.springframework.http.server.reactive.ServerHttpRequest;
11 | import org.springframework.http.server.reactive.ServerHttpResponse;
12 | import org.springframework.web.server.ServerWebExchange;
13 | import reactor.core.publisher.Mono;
14 |
15 | import java.util.Map;
16 |
17 | /**
18 | * 响应 TraceId 处理器
19 | *
20 | * @author L.cm
21 | */
22 | public class TraceGatewayFilter implements GlobalFilter, Ordered {
23 |
24 | @Override
25 | public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
26 | // 1. 传递 新的header
27 | Map copyOfContextMap = TraceContentFactory.assemblyTraceContentStatic();
28 | ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
29 | builder.headers(httpHeaders -> {
30 | for (Map.Entry copyOfContext : copyOfContextMap.entrySet()) {
31 | httpHeaders.set(copyOfContext.getKey(), copyOfContext.getValue());
32 | }
33 | });
34 | ServerHttpResponse response = exchange.getResponse();
35 | // 2. 获取 traceId
36 | String traceId = MDC.get(Constants.LEGACY_TRACE_ID_NAME);
37 | // 3. 处理响应的 header traceId
38 | HttpHeaders responseHeaders = response.getHeaders();
39 | if (traceId != null) {
40 | responseHeaders.set(Constants.LEGACY_TRACE_ID_NAME, traceId);
41 | }
42 | return chain.filter(exchange.mutate().request(builder.build()).build());
43 | }
44 |
45 | @Override
46 | public int getOrder() {
47 | return Ordered.HIGHEST_PRECEDENCE + 1;
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/instrument/reactive/TraceReactiveConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.instrument.reactive;
2 |
3 | import com.pig4cloud.trace.TraceLogProperties;
4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 |
8 | /**
9 | * @author purgeyao
10 | * @since 1.0
11 | */
12 | @Configuration(proxyBeanMethods = false)
13 | @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
14 | public class TraceReactiveConfiguration {
15 |
16 | @Bean
17 | public TraceReactiveFilter traceReactiveFilter(TraceLogProperties traceLogProperties) {
18 | return new TraceReactiveFilter(traceLogProperties);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/instrument/reactive/TraceReactiveFilter.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.instrument.reactive;
2 |
3 | import com.pig4cloud.trace.TraceContentFactory;
4 | import com.pig4cloud.trace.TraceLogProperties;
5 | import com.pig4cloud.trace.util.URLUtil;
6 | import org.slf4j.MDC;
7 | import org.springframework.boot.web.reactive.filter.OrderedWebFilter;
8 | import org.springframework.core.Ordered;
9 | import org.springframework.http.HttpHeaders;
10 | import org.springframework.http.server.reactive.ServerHttpRequest;
11 | import org.springframework.util.StringUtils;
12 | import org.springframework.web.server.ServerWebExchange;
13 | import org.springframework.web.server.WebFilterChain;
14 | import reactor.core.publisher.Mono;
15 |
16 | import java.util.HashMap;
17 | import java.util.Map;
18 | import java.util.Set;
19 |
20 | /**
21 | * spring webflux 或者 spring cloud gateway 使用
22 | *
23 | * @author L.cm
24 | */
25 | public class TraceReactiveFilter implements OrderedWebFilter {
26 |
27 | private final TraceLogProperties traceLogProperties;
28 |
29 | public TraceReactiveFilter(TraceLogProperties traceLogProperties) {
30 | this.traceLogProperties = traceLogProperties;
31 | }
32 |
33 | @Override
34 | public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
35 | ServerHttpRequest request = exchange.getRequest();
36 | HttpHeaders headers = request.getHeaders();
37 |
38 | Map formatMap = new HashMap<>(16);
39 | // 获取自定义参数
40 | Set expandFormat = traceLogProperties.getFormat();
41 | for (String k : expandFormat) {
42 | String v = headers.getFirst(k);
43 | if (StringUtils.hasText(v)) {
44 | formatMap.put(k, URLUtil.decode(v));
45 | }
46 | }
47 |
48 | // 写入 MDC
49 | TraceContentFactory.storageMDC(formatMap);
50 | return chain.filter(exchange).doFinally(t -> MDC.clear());
51 | }
52 |
53 | @Override
54 | public int getOrder() {
55 | return Ordered.HIGHEST_PRECEDENCE;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/instrument/resttemplate/RestTemplatePostProcessor.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.instrument.resttemplate;
2 |
3 | import com.pig4cloud.trace.TraceContentFactory;
4 | import org.springframework.beans.BeansException;
5 | import org.springframework.beans.factory.config.BeanPostProcessor;
6 | import org.springframework.boot.web.client.RestTemplateBuilder;
7 | import org.springframework.core.Ordered;
8 | import org.springframework.core.PriorityOrdered;
9 | import org.springframework.http.client.ClientHttpRequestInterceptor;
10 | import org.springframework.web.client.RestTemplate;
11 |
12 | import java.util.List;
13 |
14 | /**
15 | * @author purgeyao
16 | * @since 1.0
17 | */
18 | public class RestTemplatePostProcessor implements BeanPostProcessor, PriorityOrdered {
19 |
20 | private final TraceContentFactory traceContentFactory;
21 |
22 | public RestTemplatePostProcessor(TraceContentFactory traceContentFactory) {
23 | this.traceContentFactory = traceContentFactory;
24 | }
25 |
26 | @Override
27 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
28 | if (bean instanceof RestTemplateBuilder) {
29 | RestTemplate restTemplate = ((RestTemplateBuilder) bean).build();
30 | processing(restTemplate);
31 | }
32 | if (bean instanceof RestTemplate) {
33 | RestTemplate restTemplate = (RestTemplate) bean;
34 | processing(restTemplate);
35 | }
36 | return bean;
37 | }
38 |
39 | @Override
40 | public int getOrder() {
41 | return Ordered.HIGHEST_PRECEDENCE + 1;
42 | }
43 |
44 | private void processing(RestTemplate restTemplate) {
45 | List interceptors = restTemplate.getInterceptors();
46 | interceptors.add(new TraceClientHttpRequestInterceptor(traceContentFactory));
47 | restTemplate.setInterceptors(interceptors);
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/instrument/resttemplate/TraceClientHttpRequestInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.instrument.resttemplate;
2 |
3 | import com.pig4cloud.trace.TraceContentFactory;
4 | import org.springframework.http.HttpHeaders;
5 | import org.springframework.http.HttpRequest;
6 | import org.springframework.http.client.ClientHttpRequestExecution;
7 | import org.springframework.http.client.ClientHttpRequestInterceptor;
8 | import org.springframework.http.client.ClientHttpResponse;
9 |
10 | import java.io.IOException;
11 | import java.util.Map;
12 |
13 | /**
14 | * @author purgeyao
15 | * @since 1.0
16 | */
17 | public class TraceClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
18 |
19 | private final TraceContentFactory traceContentFactory;
20 |
21 | public TraceClientHttpRequestInterceptor(TraceContentFactory traceContentFactory) {
22 | this.traceContentFactory = traceContentFactory;
23 | }
24 |
25 | @Override
26 | public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes,
27 | ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
28 | HttpHeaders headers = httpRequest.getHeaders();
29 | Map copyOfContextMap = traceContentFactory.assemblyTraceContent();
30 | for (Map.Entry copyOfContext : copyOfContextMap.entrySet()) {
31 | headers.add(copyOfContext.getKey(), copyOfContext.getValue());
32 | }
33 | return clientHttpRequestExecution.execute(httpRequest, bytes);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/instrument/resttemplate/TraceRestTemplateConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.instrument.resttemplate;
2 |
3 | import com.pig4cloud.trace.TraceContentFactory;
4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.web.client.RestTemplate;
8 |
9 | /**
10 | * @author purgeyao
11 | * @since 1.0
12 | */
13 | @Configuration(proxyBeanMethods = false)
14 | @ConditionalOnClass({ RestTemplate.class, TraceContentFactory.class })
15 | public class TraceRestTemplateConfiguration {
16 |
17 | @Bean
18 | public RestTemplatePostProcessor restTemplatePostProcessor(TraceContentFactory traceContentFactory) {
19 | return new RestTemplatePostProcessor(traceContentFactory);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/instrument/servlet/TraceServletConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.instrument.servlet;
2 |
3 | import com.pig4cloud.trace.TraceLogProperties;
4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 |
8 | /**
9 | * @author purgeyao
10 | * @since 1.0
11 | */
12 | @Configuration(proxyBeanMethods = false)
13 | @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
14 | public class TraceServletConfiguration {
15 |
16 | @Bean
17 | public TraceServletFilter traceServletFilter(TraceLogProperties traceLogProperties) {
18 | return new TraceServletFilter(traceLogProperties);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/instrument/servlet/TraceServletFilter.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.instrument.servlet;
2 |
3 | import com.pig4cloud.trace.TraceContentFactory;
4 | import com.pig4cloud.trace.TraceLogProperties;
5 | import com.pig4cloud.trace.util.URLUtil;
6 | import jakarta.servlet.*;
7 | import jakarta.servlet.http.HttpServletRequest;
8 | import org.slf4j.MDC;
9 | import org.springframework.util.StringUtils;
10 |
11 | import java.io.IOException;
12 | import java.util.HashMap;
13 | import java.util.Map;
14 | import java.util.Set;
15 |
16 | /**
17 | * 请求拦截器 初始化 Trace 内容
18 | *
19 | * @author purgeyao
20 | * @since 1.0.0
21 | */
22 | public class TraceServletFilter implements Filter {
23 |
24 | private final TraceLogProperties traceLogProperties;
25 |
26 | public TraceServletFilter(TraceLogProperties traceLogProperties) {
27 | this.traceLogProperties = traceLogProperties;
28 | }
29 |
30 | @Override
31 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
32 | throws IOException, ServletException {
33 | HttpServletRequest request = ((HttpServletRequest) servletRequest);
34 |
35 | Map formatMap = new HashMap<>(16);
36 | // 获取自定义参数
37 | Set expandFormat = traceLogProperties.getFormat();
38 | for (String k : expandFormat) {
39 | String v = request.getHeader(k);
40 | if (StringUtils.hasText(v)) {
41 | formatMap.put(k, URLUtil.decode(v));
42 | }
43 | }
44 |
45 | // 写入 MDC
46 | TraceContentFactory.storageMDC(formatMap);
47 | try {
48 | filterChain.doFilter(servletRequest, servletResponse);
49 | }
50 | finally {
51 | MDC.clear();
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/metadata/TraceFormatInfo.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.metadata;
2 |
3 | import java.util.Objects;
4 |
5 | /**
6 | * 日志格式 信息
7 | *
8 | * @author purgeyao
9 | * @since 1.0.0
10 | */
11 | public class TraceFormatInfo {
12 |
13 | /**
14 | * 格式字段
15 | */
16 | private String formatField;
17 |
18 | /**
19 | * 字段显示内容
20 | */
21 | private String fieldVal;
22 |
23 | public String getFormatField() {
24 | return formatField;
25 | }
26 |
27 | public void setFormatField(String formatField) {
28 | this.formatField = formatField;
29 | }
30 |
31 | public String getFieldVal() {
32 | return fieldVal;
33 | }
34 |
35 | public void setFieldVal(String fieldVal) {
36 | this.fieldVal = fieldVal;
37 | }
38 |
39 | @Override
40 | public boolean equals(Object o) {
41 | if (this == o) {
42 | return true;
43 | }
44 | if (o == null || getClass() != o.getClass()) {
45 | return false;
46 | }
47 | TraceFormatInfo that = (TraceFormatInfo) o;
48 | return Objects.equals(formatField, that.formatField) && Objects.equals(fieldVal, that.fieldVal);
49 | }
50 |
51 | @Override
52 | public int hashCode() {
53 | return Objects.hash(formatField, fieldVal);
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/metadata/TraceFormatInfoHelper.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.metadata;
2 |
3 | import java.util.Set;
4 | import java.util.concurrent.CopyOnWriteArraySet;
5 |
6 | /**
7 | * Trace log 格式 辅助类
8 | *
9 | * @author purgeyao
10 | * @since 1.0.0
11 | */
12 | public class TraceFormatInfoHelper {
13 |
14 | /**
15 | * 储存日志格式类表信息
16 | */
17 | private static final Set TRACE_FORMAT_INFO_CACHE = new CopyOnWriteArraySet<>();
18 |
19 | /**
20 | * 添加日志信息
21 | * @param formatInfo 日志格式对象
22 | */
23 | public static void addTraceFormatInfo(TraceFormatInfo formatInfo) {
24 | TRACE_FORMAT_INFO_CACHE.add(formatInfo);
25 | }
26 |
27 | public static Set getTraceFormatInfos() {
28 | return TRACE_FORMAT_INFO_CACHE;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/processor/TraceEnvironmentPostProcessor.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.processor;
2 |
3 | import com.pig4cloud.trace.Constants;
4 | import com.pig4cloud.trace.TraceFormatEnum;
5 | import com.pig4cloud.trace.util.EnvironmentUtils;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.env.EnvironmentPostProcessor;
8 | import org.springframework.core.env.ConfigurableEnvironment;
9 | import org.springframework.core.env.Environment;
10 | import org.springframework.util.StringUtils;
11 |
12 | import java.util.*;
13 |
14 | /**
15 | * 日志格式处理器
16 | *
17 | * @author purgeyao
18 | * @since 1.0.0
19 | */
20 | public class TraceEnvironmentPostProcessor implements EnvironmentPostProcessor {
21 |
22 | private static final String PROPERTY_SOURCE_NAME = "defaultProperties";
23 |
24 | private static final String LEVEL_STR_ORIGINAL = "%5p [${spring.application.name:-},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]";
25 |
26 | private static final String LEVEL_STR_PARENT = "%5p [${spring.application.name:-},%X{X-B3-TraceId:-},%X{X-B3-ParentName:-}]";
27 |
28 | @Override
29 | public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
30 | List envList = getFormatEnv(environment);
31 | Map map = new HashMap<>(1);
32 | map.put("logging.pattern.level", assemblyLevel(envList));
33 | EnvironmentUtils.addOrReplace(environment.getPropertySources(), map, PROPERTY_SOURCE_NAME);
34 | }
35 |
36 | /**
37 | * 获取 spring.trace.log.format list 对象
38 | * @param environment env对象
39 | * @return
40 | */
41 | private List getFormatEnv(Environment environment) {
42 | List result = new ArrayList<>();
43 | // 获取自定义格式
44 | String format = environment.getProperty("spring.trace.log.format");
45 | // 判断是否为空 返回默认格式
46 | if (StringUtils.isEmpty(format)) {
47 | result.add(TraceFormatEnum.LOCAL_NAME.getValue());
48 | result.add(TraceFormatEnum.TRACE_ID.getValue());
49 | result.add(TraceFormatEnum.PARENT_NAME.getValue());
50 | }
51 | else {
52 | result.addAll(Arrays.asList(format.split(",")));
53 | }
54 | return result;
55 | }
56 |
57 | /**
58 | * 处理输出格式 logging.pattern.level
59 | * @param result 格式集合
60 | * @return 日志格式
61 | */
62 | private String assemblyLevel(List result) {
63 | // 判断是否为空 返回默认格式
64 | if (result.isEmpty()) {
65 | return LEVEL_STR_PARENT;
66 | }
67 | // 拼接 格式字符串
68 | StringBuilder sb = new StringBuilder("%5p [");
69 | for (String value : result) {
70 | if (Constants.LOCAL_NAME.equals(value)) {
71 | sb.append("${").append(value).append(":-}").append(",");
72 | }
73 | else if (TraceFormatEnum.LOCAL_NAME.name().equals(value)) {
74 | sb.append("${").append(Constants.LOCAL_NAME).append(":-}").append(",");
75 | }
76 | else {
77 | sb.append("%X{").append(value).append(":-}").append(",");
78 | }
79 | }
80 | sb.deleteCharAt(sb.length() - 1).append("]");
81 | return sb.toString();
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/util/EnvironmentUtils.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.util;
2 |
3 | import org.springframework.core.env.MapPropertySource;
4 | import org.springframework.core.env.MutablePropertySources;
5 | import org.springframework.core.env.PropertySource;
6 |
7 | import java.util.Map;
8 |
9 | /**
10 | * 配置 util
11 | *
12 | * @author purgeyao
13 | * @since 1.0.0
14 | */
15 | public class EnvironmentUtils {
16 |
17 | public static void addOrReplace(MutablePropertySources propertySources, Map map,
18 | String propertySourceName) {
19 | MapPropertySource target = null;
20 | if (propertySources.contains(propertySourceName)) {
21 | PropertySource> source = propertySources.get(propertySourceName);
22 | if (source instanceof MapPropertySource) {
23 | target = (MapPropertySource) source;
24 | for (String key : map.keySet()) {
25 | if (!target.containsProperty(key)) {
26 | target.getSource().put(key, map.get(key));
27 | }
28 | }
29 | }
30 | }
31 | if (target == null) {
32 | target = new MapPropertySource(propertySourceName, map);
33 | }
34 | if (!propertySources.contains(propertySourceName)) {
35 | propertySources.addLast(target);
36 | }
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/util/TraceIdUtil.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.util;
2 |
3 | import java.util.UUID;
4 |
5 | /**
6 | * traceId 处理
7 | *
8 | * @author purgeyao
9 | * @since 1.0.0
10 | */
11 | public class TraceIdUtil {
12 |
13 | /**
14 | * 生产 traceId
15 | * @return traceId
16 | */
17 | public static String traceIdString() {
18 | UUID uuid = UUID.randomUUID();
19 | String uuidStr = uuid.toString().replace("-", "");
20 | return getUUID(uuidStr, 16);
21 | }
22 |
23 | /**
24 | * 处理 traceId 长度
25 | * @param uuid 原始uuid
26 | * @param len 长度
27 | * @return traceId
28 | */
29 | public static String getUUID(String uuid, int len) {
30 | if (0 >= len) {
31 | return null;
32 | }
33 | StringBuilder sb = new StringBuilder();
34 | for (int i = 0; i < len; i++) {
35 | sb.append(uuid.charAt(i));
36 | }
37 | return sb.toString();
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/pig4cloud/trace/util/URLUtil.java:
--------------------------------------------------------------------------------
1 | package com.pig4cloud.trace.util;
2 |
3 | import java.io.UnsupportedEncodingException;
4 | import java.net.URLDecoder;
5 |
6 | /**
7 | * url 工具
8 | *
9 | * @author L.cm
10 | */
11 | public class URLUtil {
12 |
13 | /**
14 | * url 解码
15 | * @param url url
16 | * @return String
17 | */
18 | public static String decode(String url) {
19 | try {
20 | return URLDecoder.decode(url, "UTF-8");
21 | }
22 | catch (UnsupportedEncodingException e) {
23 | throw new IllegalArgumentException(e);
24 | }
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | # Auto Configuration
2 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
3 | com.pig4cloud.trace.TraceAutoConfiguration,\
4 | com.pig4cloud.trace.instrument.servlet.TraceServletConfiguration,\
5 | com.pig4cloud.trace.instrument.reactive.TraceReactiveConfiguration,\
6 | com.pig4cloud.trace.instrument.gateway.TraceGatewayAutoConfiguration,\
7 | com.pig4cloud.trace.instrument.feign.TraceFeignClientAutoConfiguration,\
8 | com.pig4cloud.trace.instrument.resttemplate.TraceRestTemplateConfiguration
9 |
10 | # Environment Post Processor
11 | org.springframework.boot.env.EnvironmentPostProcessor=\
12 | com.pig4cloud.trace.processor.TraceEnvironmentPostProcessor
13 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports:
--------------------------------------------------------------------------------
1 | com.pig4cloud.trace.TraceAutoConfiguration
2 | com.pig4cloud.trace.instrument.servlet.TraceServletConfiguration
3 | com.pig4cloud.trace.instrument.reactive.TraceReactiveConfiguration
4 | com.pig4cloud.trace.instrument.gateway.TraceGatewayAutoConfiguration
5 | com.pig4cloud.trace.instrument.feign.TraceFeignClientAutoConfiguration
6 | com.pig4cloud.trace.instrument.resttemplate.TraceRestTemplateConfiguration
7 |
--------------------------------------------------------------------------------