├── .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 | 1709779710 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 | --------------------------------------------------------------------------------