Merge branch 'opengoofy:develop' into develop

pull/1490/head
peiht 2 years ago committed by GitHub
commit 0d7811ac5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,35 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
coverage:
status:
project:
default:
target: auto
threshold: 0%
informational: true
patch:
default:
informational: true
ignore:
- "examples/.*"
- "docs/.*"
- "tests/.*"
- "checkstyle/.*"
- "format/.*"
- "threadpool/console-new/.*"
- "threadpool/console/.*"

@ -1,3 +1,3 @@
# These are supported funding model platforms
custom: ['https://hippo4j.cn/docs/community/sponsor']
custom: ['https://hippo4j.cn/community/sponsor']

@ -1,27 +0,0 @@
---
name: "BUG 报告"
about: 提交问题缺陷帮助我们更好的改进
---
## BUG 报告
在开始报告错误之前,请确保认真查看了以下步骤:
- 搜索打开和关闭的 [GitHub 问题](https://github.com/opengoofy/hippo4j/issues)
- 阅读 [常见问题文档](https://hippo4j.cn/docs/user_docs/other/issue)
请在提交问题之前回答这些问题,谢谢。
### 你使用了哪个项目hippo4j config 还是 hippo4j server?
### 你使用了哪个版本?
### 预期行为
### 实际行为
### 原因分析(如果可以)
### 问题重现步骤
### 用于重现此问题的示例代码(例如 GitHub 链接)

@ -0,0 +1,112 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
name: Bug report
title: "[Bug] Bug title "
description: If something isn't working as expected.
labels: [ "type: bug" ]
body:
- type: markdown
attributes:
value: |
For better global communication, Please write in English.
- type: checkboxes
attributes:
label: Search before asking
description: >
Please make sure to search in the [issues](https://github.com/opengoofy/hippo4j/issues)
first to see whether the same issue was reported already.
options:
- label: >
I had searched in the [issues](https://github.com/opengoofy/hippo4j/issues) and found
no similar issues.
required: true
- type: dropdown
attributes:
label: Environment
description: Describe the environment.
options:
- Mac
- Windows
- Linux
- Other
validations:
required: true
- type: dropdown
attributes:
label: Hippo4j version
description: Describe the Hippo4j version.
options:
- develop
- 1.5.0
- 1.4.3
- Other
validations:
required: true
- type: textarea
attributes:
label: What happened
description: Describe what happened.
placeholder: >
A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: How to reproduce
description: >
Describe the steps to reproduce the bug here.
placeholder: >
Please make sure you provide a reproducible step-by-step case of how to reproduce the problem
as minimally and precisely as possible.
validations:
required: true
- type: textarea
attributes:
label: Debug logs
description: Anything else we need to know?
placeholder: >
Add your debug logs here.
validations:
required: false
- type: checkboxes
attributes:
label: Are you willing to submit PR?
description: >
This is absolutely not required, but we are happy to guide you in the contribution process
especially if you already have a good understanding of how to implement the fix.
options:
- label: Yes I am willing to submit a PR!
- type: checkboxes
attributes:
label: Code of Conduct
description: >
The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it..
options:
- label: I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct) *
- type: markdown
attributes:
value: "Thanks for completing our form!"

@ -0,0 +1,68 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
name: Documentation Related
title: "[Doc] Documentation Related "
description: I find some issues related to the documentation.
labels: [ "type: documentation" ]
body:
- type: markdown
attributes:
value: |
For better global communication, Please write in English.
- type: checkboxes
attributes:
label: Search before asking
description: >
Please make sure to search in the [issues](https://github.com/opengoofy/hippo4j/issues)
first to see whether the same issue was reported already.
options:
- label: >
I had searched in the [issues](https://github.com/opengoofy/hippo4j/issues) and found
no similar issues.
required: true
- type: textarea
attributes:
label: Documentation Related
description: Describe the suggestion about document.
placeholder: >
e.g There is a typo
validations:
required: true
- type: checkboxes
attributes:
label: Are you willing to submit PR?
description: >
This is absolutely not required, but we are happy to guide you in the contribution process
especially if you already have a good understanding of how to implement the fix.
options:
- label: Yes I am willing to submit a PR!
- type: checkboxes
attributes:
label: Code of Conduct
description: >
The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it..
options:
- label: I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct) *
- type: markdown
attributes:
value: "Thanks for completing our form!"

@ -0,0 +1,77 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
name: Enhancement Request
title: "[Enhancement] Enhancement title"
description: I want to suggest an enhancement for this project
labels: [ "type: enhancement" ]
body:
- type: markdown
attributes:
value: |
For better global communication, Please write in English.
- type: checkboxes
attributes:
label: Search before asking
description: >
Please make sure to search in the [issues](https://github.com/opengoofy/hippo4j/issues)
first to see whether the same issue was reported already.
options:
- label: >
I had searched in the [issues](https://github.com/opengoofy/hippo4j/issues) and found
no similar issues.
required: true
- type: textarea
attributes:
label: Enhancement Request
description: Describe the suggestion.
placeholder: >
First of all: Have you checked the docs https://hippo4j.cn/community/dev_convention/code,
or GitHub issues whether someone else has already reported your issue?
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
description: Describe the suggestion.
placeholder: >
A clear and concise description of what you want to happen. Add any considered drawbacks.
validations:
required: true
- type: checkboxes
attributes:
label: Are you willing to submit PR?
description: >
This is absolutely not required, but we are happy to guide you in the contribution process
especially if you already have a good understanding of how to implement the fix.
options:
- label: Yes I am willing to submit a PR!
- type: checkboxes
attributes:
label: Code of Conduct
description: >
The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it..
options:
- label: I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct) *
- type: markdown
attributes:
value: "Thanks for completing our form!"

@ -1,12 +0,0 @@
---
name: "需求建议"
about: 提出针对本项目的想法和建议
---
## 需求建议
请在提交问题之前回答这些问题,谢谢。
### 您的功能请求是否与问题有关?
### 描述你想要的功能

@ -0,0 +1,70 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
name: Feature Request
title: "[Feature] Feature title "
description: I want to suggest a feature for this project.
labels: [ "type: feature" ]
body:
- type: markdown
attributes:
value: |
For better global communication, Please write in English.
- type: checkboxes
attributes:
label: Search before asking
description: >
Please make sure to search in the [issues](https://github.com/opengoofy/hippo4j/issues)
first to see whether the same issue was reported already.
options:
- label: >
I had searched in the [issues](https://github.com/opengoofy/hippo4j/issues) and found
no similar issues.
required: true
- type: textarea
attributes:
label: Feature Request
description: Describe the feature.
placeholder: >
First of all: Have you checked the docs https://hippo4j.cn/community/dev_convention/code,
or GitHub issues whether someone else has already reported your issue?
Maybe the feature already exists?
validations:
required: true
- type: checkboxes
attributes:
label: Are you willing to submit PR?
description: >
This is absolutely not required, but we are happy to guide you in the contribution process
especially if you already have a good understanding of how to implement the fix.
options:
- label: Yes I am willing to submit a PR!
- type: checkboxes
attributes:
label: Code of Conduct
description: >
The Code of Conduct helps create a safe space for everyone. We require that everyone agrees to it..
options:
- label: I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct) *
- type: markdown
attributes:
value: "Thanks for completing our form!"

@ -1,27 +0,0 @@
---
name: "问题支持"
about: 文档或讨论中未回答的使用问题
---
## 问题支持
在开始报告问题之前,请确保认真查看了以下步骤:
- 搜索打开和关闭的 [GitHub 问题](https://github.com/opengoofy/hippo4j/issues)
- 阅读 [常见问题文档](https://hippo4j.cn/docs/user_docs/other/issue)
请在提交问题之前回答这些问题,谢谢。
### 你使用了哪个项目hippo4j config 还是 hippo4j server?
### 你使用了哪个版本?
### 预期行为
### 实际行为
### 原因分析(如果可以)
### 问题重现步骤
### 用于重现此问题的示例代码(例如 GitHub 链接)

@ -0,0 +1,51 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
name: Question
title: "[Question] Question title "
description: I have a question that isn't answered in docs or issue.
labels: [ "type: question" ]
body:
- type: markdown
attributes:
value: |
For better global communication, Please write in English.
- type: checkboxes
attributes:
label: Search before asking
description: >
Please make sure to search in the [issues](https://github.com/opengoofy/hippo4j/issues)
first to see whether the same issue was reported already.
options:
- label: >
I had searched in the [issues](https://github.com/opengoofy/hippo4j/issues) and found
no similar issues.
required: true
- type: textarea
attributes:
label: Question
description: Describe your question.
placeholder: >
Describe your question here :D
validations:
required: true
- type: markdown
attributes:
value: "Thanks for completing our form!"

@ -0,0 +1,86 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
name: Unit Test
title: "[Unit Test] Unit test title"
description: I want to do some unit tests for this project
labels: [ "type: testing" ]
body:
- type: markdown
attributes:
value: |
For better global communication, Please write in English.
- type: checkboxes
attributes:
label: Search before asking
description: >
Please make sure to search in the [issues](https://github.com/opengoofy/hippo4j/issues)
first to see whether the same issue was reported already.
options:
- label: >
I had searched in the [issues](https://github.com/opengoofy/hippo4j/issues) and found
no similar issues.
required: true
- type: checkboxes
attributes:
label: Read the unit testing guidelines
description: >
Read the [develop guidelines](https://hippo4j.cn/community/dev_convention/code) before writing unit test code.
options:
- label: >
I have read.
required: true
- type: textarea
attributes:
label: Unit test request
description: Describe the unit test.
placeholder: >
First of all: Have you checked the docs https://hippo4j.cn/community/category/贡献规约,
or GitHub issues whether someone else has already reported your issue?
Maybe the unit tests you want to do have already been done?
validations:
required: true
- type: textarea
attributes:
label: Describe the unit tests you want to do
description: Describe the unit test.
value: |
Module name:
Located at:
Task status: ×(unfinished) / √(finished)
| Task Status | Class | Type |
| :------: | :------ | :------ |
| × | xxxxxx | xxxxxx |
validations:
required: true
- type: checkboxes
attributes:
label: Are you willing to submit PR?
description: >
This is absolutely not required, but we are happy to guide you in the contribution process
especially if you already have a good understanding of how to implement the fix.
options:
- label: Yes I am willing to submit a PR!
- type: markdown
attributes:
value: "Thanks for completing our form!"

@ -3,4 +3,4 @@ Fixes #ISSUSE_ID
Changes proposed in this pull request:
-
> Check mailbox configuration when submitting. [Contributor Guide](https://hippo4j.cn/docs/community/contributor)
> Check mailbox configuration when submitting. [Contributor Guide](https://hippo4j.cn/community/contributor-guide)

@ -18,7 +18,7 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Continuous Integration
name: build
on:
push:
@ -64,4 +64,26 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build with Maven
run: echo y | mvn clean install -Dskip.gpg=true -Dspotless.apply.skip=true
run: echo y | mvn clean install -Dskip.gpg=true -Dspotless.apply.skip=true -Dmaven.javadoc.skip=true
test-coverage:
if: github.repository == 'opengoofy/hippo4j'
name: Test coverage report
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Cache Maven Repos
uses: actions/cache@v3
with:
path: ~/.m2/repository
key: hippo4j-maven-third-party-${{ hashFiles('**/pom.xml') }}
restore-keys: |
hippo4j-maven-third-party-
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: 8
- name: Test with Maven
run: echo y | mvn -T1C clean install -Dskip.gpg=true -Dspotless.apply.skip=true -Dskip.jacoco.plugin=false
- name: Upload to Codecov
run: bash <(curl -s https://codecov.io/bash)

@ -21,7 +21,7 @@
on:
push:
branches:
- develop
- main
jobs:
contrib-readme-job:
@ -33,8 +33,8 @@ jobs:
with:
image_size: 50
columns_per_row: 9
committer_email: m7798432@163.com
committer_username: pirme
committer_email: wechat202110@163.com
committer_username: hippo4jbot[bot]
commit_message: 'Update the list of contributors'
env:
GITHUB_TOKEN: ${{ secrets.ACTION_TOKEN }}

16
.gitignore vendored

@ -3,6 +3,12 @@ target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
!**/node_modules/
!**/dist/
### Agent ###
hippo4j-agent/
**/dependency-reduced-pom.xml
### STS ###
.apt_generated
@ -30,7 +36,7 @@ build/
!**/src/test/**/build/
### VS Code ###
.vscode/
# .vscode/
# maven plugin ignore
*.gpg
@ -40,3 +46,11 @@ build/
docs/node_modules
docs/build
docs/.docusaurus
### Docker ###
docker/conf
docker/target
### OS ###
.DS_Store

@ -0,0 +1,41 @@
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnPaste": true,
"editor.formatOnSave": true
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnPaste": true,
"editor.formatOnSave": true
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnPaste": true,
"editor.formatOnSave": true
},
"[less]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnPaste": true,
"editor.formatOnSave": true
},
"typescript.tsdk": "./node_modules/typescript/lib"
}

@ -0,0 +1,80 @@
# Dynamic and observable thread pool framework
[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)
[![Build Status](https://github.com/opengoofy/hippo4j/actions/workflows/ci.yml/badge.svg?event=push)](https://github.com/opengoofy.hippo4j)
![](https://img.shields.io/github/stars/opengoofy/hippo4j?color=5470c6)
![](https://img.shields.io/github/forks/opengoofy/hippo4j?color=3ba272)
![](https://img.shields.io/github/contributors/opengoofy/hippo4j)
[![Docker Pulls](https://img.shields.io/docker/pulls/hippo4j/hippo4j-server.svg?label=docker%20pulls&color=fac858)](https://store.docker.com/community/images/hippo4j/hippo4j-server)
[![codecov](https://codecov.io/gh/opengoofy/hippo4j/branch/develop/graph/badge.svg?token=WBUVJN107I)](https://codecov.io/gh/opengoofy/hippo4j)
[![EN doc](https://img.shields.io/badge/readme-English-orange.svg)](https://github.com/opengoofy/hippo4j/blob/develop/README-EN.md)
| **Stargazers Over Time** | **Contributors Over Time** |
|:---------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
| [![Stargazers over time](https://api.star-history.com/svg?repos=opengoofy/hippo4j&type=Date)](https://api.star-history.com/svg?repos=opengoofy/hippo4j&type=Date) | [![Contributor over time](https://contributor-graph-api.apiseven.com/contributors-svg?chart=contributorOverTime&repo=opengoofy/hippo4j)](https://www.apiseven.com/en/contributor-graph?chart=contributorOverTime&repo=opengoofy/hippo4j) |
### Thread pool pain points
---
A thread pool is a tool for managing threads based on the idea of pooling.
Using a thread pool reduces the overhead of creating and destroying threads and avoids running out of system resources due to too many threads.
The use of thread pools is essential in highly concurrent and high-volume task processing scenarios.
If you have actually used thread pools in your projects, I believe you may have encountered the following pain points:
- Thread pools are defined randomly, with too many thread resources, causing high server load.
- The thread pool parameters are not easily evaluated and the business is at risk of failure.
- Thread pool task execution time exceeds the average execution cycle and developers are not informed.
- Thread pool tasks pile up and affect business operations.
- Wireless process pool monitoring when the service has timeouts, meltdowns, and other problems.
- Thread pools do not support the passing of runtime variables, such as MDC contexts.
- When a project is closed, a large number of running thread pool tasks are discarded.
- Thread pool running, task execution stopped, don't know the problem.
### What is Hippo4j
---
Hippo4j through the JDK thread pool enhancements, as well as extending the three-party framework underlying thread pools and other features for business systems to improve online operational security capabilities.
The following functional support is provided:
- Global Control - Managing Application Thread Pool Instances.
- Dynamic changes - dynamically changing thread pool parameters at application runtime.
- Notify alarms - Four built-in alarm notification policies.
- Run Monitoring - Real-time view of thread pool runtime data.
- Feature extensions - support for thread pooling task passing contexts, etc.
- Multiple Modes - Two built-in usage modes: Configuration Center Mode and No Middleware Mode.
- Container Management - Tomcat, Jetty, Undertow container thread pool runtime view and thread count changes.
- Framework adaptation - Dubbo, Hystrix, Polaris, RabbitMQ, RocketMQ and other consumer thread pool runtime data view and thread count changes.
### Quick Start
---
For local presentation purposes, see [Quick start](https://hippo4j.cn/docs/user_docs/user_guide/quick-start).
Demo Environment: [http://console.hippo4j.cn/index.html](http://console.hippo4j.cn/index.html).
### Who is using
---
More companies with access are welcome to register at [registration address](https://github.com/opengoofy/hippo4j/issues/13), registration is only for product promotion.
### Contributors
---
Thanks to all the developers who contributed to the project. If interested in contributing, refer to [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
<a href="https://github.com/opengoofy/hippo4j/graphs/contributors">
<img src="https://contrib.rocks/image?repo=opengoofy/hippo4j" />
</a>

@ -1,12 +1,34 @@
<img align="center" width="400" alt="image" src="https://user-images.githubusercontent.com/77398366/181906454-b46f6a14-7c2c-4b8f-8b0a-40432521bed8.png">
# 动态可观测线程池框架,提高线上运行保障能力
## 动态可观测线程池,提高系统运行保障能力
[![Gitee](https://gitee.com/itmachen/hippo4j/badge/star.svg?theme=gvp)](https://gitee.com/itmachen/hippo4j) [![GitHub](https://img.shields.io/github/stars/opengoofy/hippo4j)](https://github.com/opengoofy/hippo4j) [![Docker Pulls](https://img.shields.io/docker/pulls/hippo4j/hippo4j-server.svg)](https://store.docker.com/community/images/hippo4j/hippo4j-server) [![Contributors](https://img.shields.io/github/contributors/opengoofy/hippo4j?color=3ba272)](https://github.com/opengoofy/hippo4j/graphs/contributors) [![License](https://img.shields.io/github/license/opengoofy/hippo4j?color=5470c6)](https://github.com/opengoofy/hippo4j/blob/develop/LICENSE)
-------
[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)
[![Build Status](https://github.com/opengoofy/hippo4j/actions/workflows/ci.yml/badge.svg?event=push)](https://github.com/opengoofy/hippo4j)
## 线程池痛点
![](https://img.shields.io/github/stars/opengoofy/hippo4j?color=5470c6)
![](https://img.shields.io/github/forks/opengoofy/hippo4j?color=3ba272)
![](https://img.shields.io/github/contributors/opengoofy/hippo4j)
[![Docker Pulls](https://img.shields.io/docker/pulls/hippo4j/hippo4j-server.svg?label=docker%20pulls&color=fac858)](https://store.docker.com/community/images/hippo4j/hippo4j-server)
[![codecov](https://codecov.io/gh/opengoofy/hippo4j/branch/develop/graph/badge.svg?token=WBUVJN107I)](https://codecov.io/gh/opengoofy/hippo4j)
[![EN doc](https://img.shields.io/badge/readme-English-orange.svg)](https://github.com/opengoofy/hippo4j/blob/develop/README-EN.md)
| **Stargazers Over Time** | **Contributors Over Time** |
|:---------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
| [![Stargazers over time](https://api.star-history.com/svg?repos=opengoofy/hippo4j&type=Date)](https://api.star-history.com/svg?repos=opengoofy/hippo4j&type=Date) | [![Contributor over time](https://contributor-graph-api.apiseven.com/contributors-svg?chart=contributorOverTime&repo=opengoofy/hippo4j)](https://www.apiseven.com/en/contributor-graph?chart=contributorOverTime&repo=opengoofy/hippo4j) |
### 开源地址
---
GitHub[opengoofy/hippo4j](https://github.com/opengoofy/hippo4j)
Gitee[opengoofy/hippo4j](https://gitee.com/magestack/hippo4j)
For full documentation & more details, visit: [Docs](https://www.hippo4j.cn)
### 线程池痛点
---
线程池是一种基于池化思想管理线程的工具,使用线程池可以减少创建销毁线程的开销,避免线程过多导致系统资源耗尽。在高并发以及大批量的任务处理场景,线程池的使用是必不可少的。
@ -22,588 +44,99 @@
- 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。
- 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。
## 什么是 Hippo-4J
### 什么是 Hippo4j
Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池等功能,为业务系统提高线上运行保障能力。
---
提供以下功能支持:
- 全局管控 - 管理应用线程池实例
- 全局管控 - 管理应用线程池实例
- 动态变更 - 应用运行时动态变更线程池参数,包括不限于:核心、最大线程数、阻塞队列容量、拒绝策略等。
- 动态变更 - 应用运行时动态变更线程池参数,包括不限于:核心、最大线程数、阻塞队列容量、拒绝策略等。
- 通知报警 - 内置四种报警通知策略,线程池活跃度、容量水位、拒绝策略以及任务执行时间超长。
- 运行监控 - 实时查看线程池运行时数据,最近半小时线程池运行数据图表展示。
- 数据采集 - 支持多种方式采集线程池数据包括但不限于日志、内置采集、Prometheus、InfluxDB、ElasticSearch 等。
- 运行监控 - 实时查看线程池运行时数据,自定义时间内线程池运行数据图表展示。
- 功能扩展 - 支持线程池任务传递上下文;项目关闭时,支持等待线程池在指定时间内完成任务。
- 多种模式 - 内置两种使用模式:[依赖配置中心](https://hippo4j.cn/docs/user_docs/getting_started/config/hippo4j-config-start) 和 [无中间件依赖](https://hippo4j.cn/docs/user_docs/getting_started/server/hippo4j-server-start)。
- 容器管理 - Tomcat、Jetty、Undertow 容器线程池运行时查看和线程数变更。
- 中间件适配 - Dubbo、Hystrix、RocketMQ、RabbitMQ 等消费线程池运行时数据查看和线程数变更。
- 框架适配 - Dubbo、Hystrix、RabbitMQ、RocketMQ 等消费线程池运行时数据查看和线程数变更。
- 变更审核 - 提供多种用户角色,普通用户变更线程池参数需要 Admin 用户审核方可生效。
- 动态化插件 - 内置多种线程池插件,支持用户自定义插件以及运行时扩展。
- 多版本适配 - 经过实际测试,已支持客户端 SpringBoot 1.5.x => 2.7.5 版本(更高版本未测试)。
## 快速开始
### 架构设计
---
<img width="1307" alt="image" src="https://user-images.githubusercontent.com/106363931/233792824-f879500f-fea1-4872-be15-957236f6bf2b.png">
### 快速开始
---
对于本地演示目的,请参阅 [Quick start](https://hippo4j.cn/docs/user_docs/user_guide/quick-start)
演示环境: [http://console.hippo4j.cn/index.html](http://console.hippo4j.cn/index.html)
## 联系我
### 接入登记
---
更多接入的公司,欢迎在 [登记地址](https://github.com/opengoofy/hippo4j/issues/13) 登记,登记仅仅为了产品推广。
### 联系我
---
![](https://user-images.githubusercontent.com/77398366/185774220-c11951f9-e130-4d60-8204-afb5c51d4401.png)
开源不易,右上角点个 Star 鼓励一下吧!
扫码添加微信备注hippo4j邀您加入群聊。若图片加载不出来访问 [官网站点](https://hippo4j.cn/docs/user_docs/other/group)。
如果大家想要实时关注 Hippo4j 更新的文章以及分享的干货的话,可以关注我的公众号
## 友情链接
使用过程中有任何问题,或者对项目有什么建议,关注公众号回复:加群,和 `1000+` 志同道合的朋友交流讨论。
<img width="586" alt="image" src="https://user-images.githubusercontent.com/77398366/225888779-367f42a6-8401-4867-8e80-44214e1d17c1.png">
### 深入原理
---
如果您公司没有使用 Hippo4j 场景的话,我也建议去阅读下项目的底层原理,主要有以下几个原因:
- 为了提高代码质量以及后续的扩展行为,运用多种设计模式实现高内聚、低耦合。
- 框架底层依赖 Spring 框架运行,并在源码中大量使用 Spring 相关功能。
- 运用 JUC 并发包下多种工具保障多线程运行安全,通过实际场景理解并发编程。
- 借鉴主流开源框架 Nacos、Eureka 实现轻量级配置中心和注册中心功能。
- 自定义 RPC 框架实现,封装 Netty 完成客户端/服务端网络通信优化。
- 通过 CheckStyle、Spotless 等插件规范代码编写,保障高质量代码行为和代码样式。
### 友情链接
---
- [[ Sa-Token ]](https://github.com/dromara/sa-token):一个轻量级 java 权限认证框架,让鉴权变得简单、优雅!
- [[ HertzBeat ]](https://github.com/dromara/hertzbeat):易用友好的云监控系统, 无需 Agent, 强大自定义监控能力。
- [[ JavaGuide ]](https://github.com/Snailclimb/JavaGuide):一份涵盖大部分 Java 程序员所需要掌握的核心知识。
- [[ toBeBetterJavaer ]](https://github.com/itwanger/toBeBetterJavaer):一份通俗易懂、风趣幽默的 Java 学习指南。
- [[ Jpom ]](https://gitee.com/dromara/Jpom):简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件。
- [[ 12306 ]](https://gitee.com/nageoffer/12306):完成高仿 12306 用户+抢票+订单+支付服务,帮助学生主打就业的项目。
- [[ CongoMall ]](https://gitee.com/nageoffer/congomall):企业级商城,基于 DDD 领域驱动模型开发,包含商城业务和基础架构。
### 贡献者
## 贡献者
---
感谢所有为项目作出贡献的开发者。如果有意贡献,参考 [good first issue](https://github.com/opengoofy/hippo4j/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)。
<!-- readme: contributors -start -->
<table>
<tr>
<td align="center">
<a href="https://github.com/itmachen">
<img src="https://avatars.githubusercontent.com/u/77398366?v=4" width="50;" alt="itmachen"/>
<br />
<sub><b>小马哥</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/shining-stars-lk">
<img src="https://avatars.githubusercontent.com/u/40255310?v=4" width="50;" alt="shining-stars-lk"/>
<br />
<sub><b>Lucky 8</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/weihubeats">
<img src="https://avatars.githubusercontent.com/u/42484192?v=4" width="50;" alt="weihubeats"/>
<br />
<sub><b>Weihubeats</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/pirme">
<img src="https://avatars.githubusercontent.com/u/41976977?v=4" width="50;" alt="pirme"/>
<br />
<sub><b>李金来</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/iwangjie">
<img src="https://avatars.githubusercontent.com/u/23075587?v=4" width="50;" alt="iwangjie"/>
<br />
<sub><b>王杰</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/BigXin0109">
<img src="https://avatars.githubusercontent.com/u/24769514?v=4" width="50;" alt="BigXin0109"/>
<br />
<sub><b>BigXin0109</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/shanjianq">
<img src="https://avatars.githubusercontent.com/u/49084314?v=4" width="50;" alt="shanjianq"/>
<br />
<sub><b>Shanjianq</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/xqxyxchy">
<img src="https://avatars.githubusercontent.com/u/21134578?v=4" width="50;" alt="xqxyxchy"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Gdk666">
<img src="https://avatars.githubusercontent.com/u/22442067?v=4" width="50;" alt="Gdk666"/>
<br />
<sub><b>Null</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/pizihao">
<img src="https://avatars.githubusercontent.com/u/48643103?v=4" width="50;" alt="pizihao"/>
<br />
<sub><b>Pizihao</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/maxisvest">
<img src="https://avatars.githubusercontent.com/u/20422618?v=4" width="50;" alt="maxisvest"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/zhuanghaozhe">
<img src="https://avatars.githubusercontent.com/u/73152769?v=4" width="50;" alt="zhuanghaozhe"/>
<br />
<sub><b>庄昊哲</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/liulinfei121">
<img src="https://avatars.githubusercontent.com/u/57127515?v=4" width="50;" alt="liulinfei121"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/road2master">
<img src="https://avatars.githubusercontent.com/u/53806703?v=4" width="50;" alt="road2master"/>
<br />
<sub><b>Lijx</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Atmanuclear">
<img src="https://avatars.githubusercontent.com/u/25747005?v=4" width="50;" alt="Atmanuclear"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/hippo4j">
<img src="https://avatars.githubusercontent.com/u/93200041?v=4" width="50;" alt="hippo4j"/>
<br />
<sub><b>Hippo4j</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/imyzt">
<img src="https://avatars.githubusercontent.com/u/28680198?v=4" width="50;" alt="imyzt"/>
<br />
<sub><b>杨镇涛</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Tliutao">
<img src="https://avatars.githubusercontent.com/u/17719583?v=4" width="50;" alt="Tliutao"/>
<br />
<sub><b>Liutao</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/monsterxxp">
<img src="https://avatars.githubusercontent.com/u/37952446?v=4" width="50;" alt="monsterxxp"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/voilaf">
<img src="https://avatars.githubusercontent.com/u/16870828?v=4" width="50;" alt="voilaf"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/gywanghai">
<img src="https://avatars.githubusercontent.com/u/102774648?v=4" width="50;" alt="gywanghai"/>
<br />
<sub><b>二师兄</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/skyemin">
<img src="https://avatars.githubusercontent.com/u/38172444?v=4" width="50;" alt="skyemin"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Redick01">
<img src="https://avatars.githubusercontent.com/u/15903214?v=4" width="50;" alt="Redick01"/>
<br />
<sub><b>Redick Liu</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/xiaochengxuyuan">
<img src="https://avatars.githubusercontent.com/u/9032006?v=4" width="50;" alt="xiaochengxuyuan"/>
<br />
<sub><b>Sean Wu</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/HKMV">
<img src="https://avatars.githubusercontent.com/u/26456469?v=4" width="50;" alt="HKMV"/>
<br />
<sub><b>Serenity</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/gewuwo">
<img src="https://avatars.githubusercontent.com/u/97213587?v=4" width="50;" alt="gewuwo"/>
<br />
<sub><b>格悟沃</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/hushtian">
<img src="https://avatars.githubusercontent.com/u/55479601?v=4" width="50;" alt="hushtian"/>
<br />
<sub><b>Null</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/jinlingmei">
<img src="https://avatars.githubusercontent.com/u/24669082?v=4" width="50;" alt="jinlingmei"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/linlinjie">
<img src="https://avatars.githubusercontent.com/u/22275940?v=4" width="50;" alt="linlinjie"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/selectbook">
<img src="https://avatars.githubusercontent.com/u/8454350?v=4" width="50;" alt="selectbook"/>
<br />
<sub><b>Leping Huang</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/soulmz">
<img src="https://avatars.githubusercontent.com/u/10662992?v=4" width="50;" alt="soulmz"/>
<br />
<sub><b>Soulzz</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/tomsun28">
<img src="https://avatars.githubusercontent.com/u/24788200?v=4" width="50;" alt="tomsun28"/>
<br />
<sub><b>Tomsun28</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/backbay2-yzg">
<img src="https://avatars.githubusercontent.com/u/64394486?v=4" width="50;" alt="backbay2-yzg"/>
<br />
<sub><b>游祖光</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/puppet4">
<img src="https://avatars.githubusercontent.com/u/28887178?v=4" width="50;" alt="puppet4"/>
<br />
<sub><b>Tudo</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/2EXP">
<img src="https://avatars.githubusercontent.com/u/26007713?v=4" width="50;" alt="2EXP"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/onesimplecoder">
<img src="https://avatars.githubusercontent.com/u/30288465?v=4" width="50;" alt="onesimplecoder"/>
<br />
<sub><b>Alic</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/CalebZYC">
<img src="https://avatars.githubusercontent.com/u/42887532?v=4" width="50;" alt="CalebZYC"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Hibernate5666">
<img src="https://avatars.githubusercontent.com/u/30147527?v=4" width="50;" alt="Hibernate5666"/>
<br />
<sub><b>Cheng Xihong</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/smartdj">
<img src="https://avatars.githubusercontent.com/u/3272679?v=4" width="50;" alt="smartdj"/>
<br />
<sub><b>DJ</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/dmego">
<img src="https://avatars.githubusercontent.com/u/22118976?v=4" width="50;" alt="dmego"/>
<br />
<sub><b>Dmego</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/dousp">
<img src="https://avatars.githubusercontent.com/u/5936499?v=4" width="50;" alt="dousp"/>
<br />
<sub><b>Douspeng</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/hl1248">
<img src="https://avatars.githubusercontent.com/u/70790953?v=4" width="50;" alt="hl1248"/>
<br />
<sub><b>Lucas</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/gentlelynn">
<img src="https://avatars.githubusercontent.com/u/19168453?v=4" width="50;" alt="gentlelynn"/>
<br />
<sub><b>Lynn</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/sanliangitch">
<img src="https://avatars.githubusercontent.com/u/48200100?v=4" width="50;" alt="sanliangitch"/>
<br />
<sub><b>WuLang</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/alexhaoxuan">
<img src="https://avatars.githubusercontent.com/u/46749051?v=4" width="50;" alt="alexhaoxuan"/>
<br />
<sub><b>Alexli</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/baymax55">
<img src="https://avatars.githubusercontent.com/u/35788491?v=4" width="50;" alt="baymax55"/>
<br />
<sub><b>Baymax55</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/qizhongju">
<img src="https://avatars.githubusercontent.com/u/19883548?v=4" width="50;" alt="qizhongju"/>
<br />
<sub><b>Bug搬运工</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/san4j">
<img src="https://avatars.githubusercontent.com/u/40364355?v=4" width="50;" alt="san4j"/>
<br />
<sub><b>San4j</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/zhenyed">
<img src="https://avatars.githubusercontent.com/u/26167590?v=4" width="50;" alt="zhenyed"/>
<br />
<sub><b>Zhenye</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/dongming0920">
<img src="https://avatars.githubusercontent.com/u/57832778?v=4" width="50;" alt="dongming0920"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/f497196689">
<img src="https://avatars.githubusercontent.com/u/15325854?v=4" width="50;" alt="f497196689"/>
<br />
<sub><b>Fengjing</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Snailclimb">
<img src="https://avatars.githubusercontent.com/u/29880145?v=4" width="50;" alt="Snailclimb"/>
<br />
<sub><b>Guide</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/hbw1994">
<img src="https://avatars.githubusercontent.com/u/22744421?v=4" width="50;" alt="hbw1994"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/hncboy">
<img src="https://avatars.githubusercontent.com/u/27755574?v=4" width="50;" alt="hncboy"/>
<br />
<sub><b>Null</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/stronglong">
<img src="https://avatars.githubusercontent.com/u/15846157?v=4" width="50;" alt="stronglong"/>
<br />
<sub><b>Itermis</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/janey668">
<img src="https://avatars.githubusercontent.com/u/99872936?v=4" width="50;" alt="janey668"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/jialei-jack">
<img src="https://avatars.githubusercontent.com/u/93201205?v=4" width="50;" alt="jialei-jack"/>
<br />
<sub><b>Jialei-jack</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/klsq94">
<img src="https://avatars.githubusercontent.com/u/16208392?v=4" width="50;" alt="klsq94"/>
<br />
<sub><b>Hui Cao</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/kongyanbo-cx">
<img src="https://avatars.githubusercontent.com/u/58963923?v=4" width="50;" alt="kongyanbo-cx"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/lishiyu">
<img src="https://avatars.githubusercontent.com/u/36871640?v=4" width="50;" alt="lishiyu"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Nhxz">
<img src="https://avatars.githubusercontent.com/u/72447160?v=4" width="50;" alt="Nhxz"/>
<br />
<sub><b>Nhxz</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/op-lht">
<img src="https://avatars.githubusercontent.com/u/34021816?v=4" width="50;" alt="op-lht"/>
<br />
<sub><b>Op-lht</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/wangjie-github">
<img src="https://avatars.githubusercontent.com/u/35762878?v=4" width="50;" alt="wangjie-github"/>
<br />
<sub><b>Wangjie</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/wangyi123456">
<img src="https://avatars.githubusercontent.com/u/25248959?v=4" width="50;" alt="wangyi123456"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Williamren97">
<img src="https://avatars.githubusercontent.com/u/43086401?v=4" width="50;" alt="Williamren97"/>
<br />
<sub><b>William Ren</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/wzw8795">
<img src="https://avatars.githubusercontent.com/u/90670917?v=4" width="50;" alt="wzw8795"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/huaxianchao">
<img src="https://avatars.githubusercontent.com/u/50727527?v=4" width="50;" alt="huaxianchao"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/yangzhiw">
<img src="https://avatars.githubusercontent.com/u/13634974?v=4" width="50;" alt="yangzhiw"/>
<br />
<sub><b>Opentanent</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/yhc777">
<img src="https://avatars.githubusercontent.com/u/71164753?v=4" width="50;" alt="yhc777"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/zhaiweij">
<img src="https://avatars.githubusercontent.com/u/10173248?v=4" width="50;" alt="zhaiweij"/>
<br />
<sub><b>Zhaiweij</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/zhaojinchao95">
<img src="https://avatars.githubusercontent.com/u/33742097?v=4" width="50;" alt="zhaojinchao95"/>
<br />
<sub><b>Zhaojinchao</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/zj1997">
<img src="https://avatars.githubusercontent.com/u/31212787?v=4" width="50;" alt="zj1997"/>
<br />
<sub><b>Null</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/li-xiao-shuang">
<img src="https://avatars.githubusercontent.com/u/34903552?v=4" width="50;" alt="li-xiao-shuang"/>
<br />
<sub><b>李晓双 Li Xiao Shuang</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/oreoft">
<img src="https://avatars.githubusercontent.com/u/51789848?v=4" width="50;" alt="oreoft"/>
<br />
<sub><b>没有气的汽水</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/wo883721">
<img src="https://avatars.githubusercontent.com/u/10241323?v=4" width="50;" alt="wo883721"/>
<br />
<sub><b>Xinhao</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Createsequence">
<img src="https://avatars.githubusercontent.com/u/49221670?v=4" width="50;" alt="Createsequence"/>
<br />
<sub><b>黄成兴</b></sub>
</a>
</td></tr>
</table>
<!-- readme: contributors -end -->
### 鸣谢
---
Hippo4j 社区收到 Jetbrains 多份 Licenses并已分配项目 [活跃开发者](https://hippo4j.cn/community/team/),非常感谢 Jetbrains 对开源社区的支持。
![JetBrains Logo (Main) logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)

@ -0,0 +1,76 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# The service name in UI
# ${service name} = [${group name}::]${logic name}
# The group name is optional only.
agent.service_name=${HIPPO4J_AGENT_NAME:Your_ApplicationName}
# The agent namespace
agent.namespace=${HIPPO4J_AGENT_NAMESPACE:}
# The agent cluster
agent.cluster=${HIPPO4J_AGENT_CLUSTER:}
# If the operation name of the first span is included in this set, this segment should be ignored. Multiple values should be separated by `,`.
agent.ignore_suffix=${HIPPO4J_AGENT_IGNORE_SUFFIX:.jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg}
# If true, Hippo4j agent will save all instrumented classes files in `/debugging` folder.
# SkyWalking team may ask for these files in order to resolve compatible problem.
agent.is_open_debugging_class=${SW_AGENT_OPEN_DEBUG:false}
# If true, Hippo4j agent will cache all instrumented classes files to memory or disk files (decided by class cache mode),
# allow other javaagent to enhance those classes that enhanced by SkyWalking agent.
agent.is_cache_enhanced_class=${SW_AGENT_CACHE_CLASS:false}
# The instrumented classes cache mode: MEMORY or FILE
# MEMORY: cache class bytes to memory, if instrumented classes is too many or too large, it may take up more memory
# FILE: cache class bytes in `/class-cache` folder, automatically clean up cached class files when the application exits
agent.class_cache_mode=${SW_AGENT_CLASS_CACHE_MODE:MEMORY}
# Logging level
logging.level=${HIPPO4J_LOGGING_LEVEL:INFO}
# Logging file_name
logging.file_name=${HIPPO4J_LOGGING_FILE_NAME:hippo4j-api.log}
# Log output. Default is FILE. Use CONSOLE means output to stdout.
logging.output=${SW_LOGGING_OUTPUT:FILE}
# Log files directory. Default is blank string, meaning use "{theHippo4jAgentJarDir}/logs " to output logs.
# {theHippo4jAgentJarDir} is the directory where the hippo4j agent jar file is located
logging.dir=${HIPPO4J_LOGGING_DIR:}
# Logger resolver: PATTERN or JSON. The default is PATTERN, which uses logging.pattern to print traditional text logs.
# JSON resolver prints logs in JSON format.
logging.resolver=${HIPPO4J_LOGGING_RESOLVER:PATTERN}
# Logging format. There are all conversion specifiers:
# * %level means log level.
# * %timestamp means now of time with format yyyy-MM-dd HH:mm:ss:SSS.
# * %thread means name of current thread.
# * %msg means some message which user logged.
# * %class means SimpleName of TargetClass.
# * %throwable means a throwable which user called.
# * %agent_name means agent.service_name. Only apply to the PatternLogger.
logging.pattern=${HIPPO4J_LOGGING_PATTERN:%level %timestamp %thread %class : %msg %throwable}
# Logging max_file_size, default: 300 * 1024 * 1024 = 314572800
logging.max_file_size=${HIPPO4J_LOGGING_MAX_FILE_SIZE:314572800}
# The max history log files. When rollover happened, if log files exceed this number,
# then the oldest file will be delete. Negative or zero means off, by default.
logging.max_history_files=${HIPPO4J_LOGGING_MAX_HISTORY_FILES:-1}
# Mount the specific folders of the plugins. Plugins in mounted folders would work.
plugin.mount=${SW_MOUNT_FOLDERS:plugins,activations}
# Peer maximum description limit.
plugin.peer_max_length=${SW_PLUGIN_PEER_MAX_LENGTH:200}
# Exclude some plugins define in plugins dir.Plugin names is defined in [Agent plugin list](Plugin-list.md)
plugin.exclude_plugins=${SW_EXCLUDE_PLUGINS:}

@ -0,0 +1,232 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=======================================================================
Apache SkyWalking Subcomponents:
The Apache SkyWalking project contains subcomponents with separate copyright
notices and license terms. Your use of the source code for the these
subcomponents is subject to the terms and conditions of the following
licenses.
========================================================================
Apache 2.0 licenses
========================================================================
The following components are provided under the Apache License. See project link for details.
The text of each license is the standard Apache 2.0 license.
raphw (byte-buddy) 1.12.13: http://bytebuddy.net/ , Apache 2.0
Google: grpc-java 1.44.0: https://github.com/grpc/grpc-java, Apache 2.0
Google: gson 2.8.9: https://github.com/google/gson , Apache 2.0
Google: proto-google-common-protos 2.0.1: https://github.com/googleapis/googleapis , Apache 2.0
Google: jsr305 3.0.2: http://central.maven.org/maven2/com/google/code/findbugs/jsr305/3.0.0/jsr305-3.0.0.pom , Apache 2.0
netty 4.1.79: https://github.com/netty/netty/blob/4.1/LICENSE.txt, Apache 2.0
========================================================================
BSD licenses
========================================================================
The following components are provided under a BSD license. See project link for details.
The text of each license is also included at licenses/LICENSE-[project].txt.
asm 9.2:https://gitlab.ow2.org , BSD-3-Clause

@ -0,0 +1,299 @@
Opengoofy Hippo4j
Copyright 2017-2022 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
========================================================================
grpc-java NOTICE
========================================================================
Copyright 2014, gRPC Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-----------------------------------------------------------------------
This product contains a modified portion of 'OkHttp', an open source
HTTP & SPDY client for Android and Java applications, which can be obtained
at:
* LICENSE:
* okhttp/third_party/okhttp/LICENSE (Apache License 2.0)
* HOMEPAGE:
* https://github.com/square/okhttp
* LOCATION_IN_GRPC:
* okhttp/third_party/okhttp
This product contains a modified portion of 'Netty', an open source
networking library, which can be obtained at:
* LICENSE:
* netty/third_party/netty/LICENSE.txt (Apache License 2.0)
* HOMEPAGE:
* https://netty.io
* LOCATION_IN_GRPC:
* netty/third_party/netty
========================================================================
grpc NOTICE
========================================================================
Copyright 2014 gRPC authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
========================================================================
netty NOTICE
========================================================================
The Netty Project
=================
Please visit the Netty web site for more information:
* http://netty.io/
Copyright 2014 The Netty Project
The Netty Project licenses this file to you under the Apache License,
version 2.0 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Also, please refer to each LICENSE.<component>.txt file, which is located in
the 'license' directory of the distribution file, for the license terms of the
components that this product depends on.
-------------------------------------------------------------------------------
This product contains the extensions to Java Collections Framework which has
been derived from the works by JSR-166 EG, Doug Lea, and Jason T. Greene:
* LICENSE:
* license/LICENSE.jsr166y.txt (Public Domain)
* HOMEPAGE:
* http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/
* http://viewvc.jboss.org/cgi-bin/viewvc.cgi/jbosscache/experimental/jsr166/
This product contains a modified version of Robert Harder's Public Domain
Base64 Encoder and Decoder, which can be obtained at:
* LICENSE:
* license/LICENSE.base64.txt (Public Domain)
* HOMEPAGE:
* http://iharder.sourceforge.net/current/java/base64/
This product contains a modified portion of 'Webbit', an event based
WebSocket and HTTP server, which can be obtained at:
* LICENSE:
* license/LICENSE.webbit.txt (BSD License)
* HOMEPAGE:
* https://github.com/joewalnes/webbit
This product contains a modified portion of 'SLF4J', a simple logging
facade for Java, which can be obtained at:
* LICENSE:
* license/LICENSE.slf4j.txt (MIT License)
* HOMEPAGE:
* http://www.slf4j.org/
This product contains a modified portion of 'Apache Harmony', an open source
Java SE, which can be obtained at:
* NOTICE:
* license/NOTICE.harmony.txt
* LICENSE:
* license/LICENSE.harmony.txt (Apache License 2.0)
* HOMEPAGE:
* http://archive.apache.org/dist/harmony/
This product contains a modified portion of 'jbzip2', a Java bzip2 compression
and decompression library written by Matthew J. Francis. It can be obtained at:
* LICENSE:
* license/LICENSE.jbzip2.txt (MIT License)
* HOMEPAGE:
* https://code.google.com/p/jbzip2/
This product contains a modified portion of 'libdivsufsort', a C API library to construct
the suffix array and the Burrows-Wheeler transformed string for any input string of
a constant-size alphabet written by Yuta Mori. It can be obtained at:
* LICENSE:
* license/LICENSE.libdivsufsort.txt (MIT License)
* HOMEPAGE:
* https://github.com/y-256/libdivsufsort
This product contains a modified portion of Nitsan Wakart's 'JCTools', Java Concurrency Tools for the JVM,
which can be obtained at:
* LICENSE:
* license/LICENSE.jctools.txt (ASL2 License)
* HOMEPAGE:
* https://github.com/JCTools/JCTools
This product optionally depends on 'JZlib', a re-implementation of zlib in
pure Java, which can be obtained at:
* LICENSE:
* license/LICENSE.jzlib.txt (BSD style License)
* HOMEPAGE:
* http://www.jcraft.com/jzlib/
This product optionally depends on 'Compress-LZF', a Java library for encoding and
decoding data in LZF format, written by Tatu Saloranta. It can be obtained at:
* LICENSE:
* license/LICENSE.compress-lzf.txt (Apache License 2.0)
* HOMEPAGE:
* https://github.com/ning/compress
This product optionally depends on 'lz4', a LZ4 Java compression
and decompression library written by Adrien Grand. It can be obtained at:
* LICENSE:
* license/LICENSE.lz4.txt (Apache License 2.0)
* HOMEPAGE:
* https://github.com/jpountz/lz4-java
This product optionally depends on 'lzma-java', a LZMA Java compression
and decompression library, which can be obtained at:
* LICENSE:
* license/LICENSE.lzma-java.txt (Apache License 2.0)
* HOMEPAGE:
* https://github.com/jponge/lzma-java
This product contains a modified portion of 'jfastlz', a Java port of FastLZ compression
and decompression library written by William Kinney. It can be obtained at:
* LICENSE:
* license/LICENSE.jfastlz.txt (MIT License)
* HOMEPAGE:
* https://code.google.com/p/jfastlz/
This product contains a modified portion of and optionally depends on 'Protocol Buffers', Google's data
interchange format, which can be obtained at:
* LICENSE:
* license/LICENSE.protobuf.txt (New BSD License)
* HOMEPAGE:
* https://github.com/google/protobuf
This product optionally depends on 'Bouncy Castle Crypto APIs' to generate
a temporary self-signed X.509 certificate when the JVM does not provide the
equivalent functionality. It can be obtained at:
* LICENSE:
* license/LICENSE.bouncycastle.txt (MIT License)
* HOMEPAGE:
* http://www.bouncycastle.org/
This product optionally depends on 'Snappy', a compression library produced
by Google Inc, which can be obtained at:
* LICENSE:
* license/LICENSE.snappy.txt (New BSD License)
* HOMEPAGE:
* https://github.com/google/snappy
This product optionally depends on 'JBoss Marshalling', an alternative Java
serialization API, which can be obtained at:
* LICENSE:
* license/LICENSE.jboss-marshalling.txt (GNU LGPL 2.1)
* HOMEPAGE:
* http://www.jboss.org/jbossmarshalling
This product optionally depends on 'Caliper', Google's micro-
benchmarking framework, which can be obtained at:
* LICENSE:
* license/LICENSE.caliper.txt (Apache License 2.0)
* HOMEPAGE:
* https://github.com/google/caliper
This product optionally depends on 'Apache Commons Logging', a logging
framework, which can be obtained at:
* LICENSE:
* license/LICENSE.commons-logging.txt (Apache License 2.0)
* HOMEPAGE:
* http://commons.apache.org/logging/
This product optionally depends on 'Apache Log4J', a logging framework, which
can be obtained at:
* LICENSE:
* license/LICENSE.log4j.txt (Apache License 2.0)
* HOMEPAGE:
* http://logging.apache.org/log4j/
This product optionally depends on 'Aalto XML', an ultra-high performance
non-blocking XML processor, which can be obtained at:
* LICENSE:
* license/LICENSE.aalto-xml.txt (Apache License 2.0)
* HOMEPAGE:
* http://wiki.fasterxml.com/AaltoHome
This product contains a modified version of 'HPACK', a Java implementation of
the HTTP/2 HPACK algorithm written by Twitter. It can be obtained at:
* LICENSE:
* license/LICENSE.hpack.txt (Apache License 2.0)
* HOMEPAGE:
* https://github.com/twitter/hpack
This product contains a modified portion of 'Apache Commons Lang', a Java library
provides utilities for the java.lang API, which can be obtained at:
* LICENSE:
* license/LICENSE.commons-lang.txt (Apache License 2.0)
* HOMEPAGE:
* https://commons.apache.org/proper/commons-lang/
This product contains the Maven wrapper scripts from 'Maven Wrapper', that provides an easy way to ensure a user has everything necessary to run the Maven build.
* LICENSE:
* license/LICENSE.mvn-wrapper.txt (Apache License 2.0)
* HOMEPAGE:
* https://github.com/takari/maven-wrapper

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-agent</artifactId>
<version>${revision}</version>
</parent>
<artifactId>hippo4j-agent-bootstrap</artifactId>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<premain.class>cn.hippo4j.agent.bootstrap.Hippo4jAgent</premain.class>
<can.redefine.classes>true</can.redefine.classes>
<can.retransform.classes>true</can.retransform.classes>
<shade.net.bytebuddy.source>net.bytebuddy</shade.net.bytebuddy.source>
<shade.net.bytebuddy.target>${shade.package}.${shade.net.bytebuddy.source}</shade.net.bytebuddy.target>
</properties>
<dependencies>
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-agent-core</artifactId>
</dependency>
</dependencies>
<build>
<finalName>hippo4j-threadpool-agent</finalName>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>false</shadedArtifactAttached>
<createDependencyReducedPom>true</createDependencyReducedPom>
<createSourcesJar>true</createSourcesJar>
<shadeSourcesContent>true</shadeSourcesContent>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Premain-Class>${premain.class}</Premain-Class>
<Can-Redefine-Classes>${can.redefine.classes}</Can-Redefine-Classes>
<Can-Retransform-Classes>${can.retransform.classes}</Can-Retransform-Classes>
</manifestEntries>
</transformer>
</transformers>
<artifactSet>
<excludes>
<exclude>*:gson</exclude>
<exclude>io.grpc:*</exclude>
<exclude>io.netty:*</exclude>
<exclude>io.opencensus:*</exclude>
<exclude>com.google.*:*</exclude>
<exclude>com.google.guava:guava</exclude>
<exclude>org.checkerframework:checker-compat-qual</exclude>
<exclude>org.codehaus.mojo:animal-sniffer-annotations</exclude>
<exclude>io.perfmark:*</exclude>
<exclude>org.slf4j:*</exclude>
</excludes>
</artifactSet>
<relocations>
<relocation>
<pattern>${shade.net.bytebuddy.source}</pattern>
<shadedPattern>${shade.net.bytebuddy.target}</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
<artifact>net.bytebuddy:byte-buddy</artifact>
<excludes>
<exclude>META-INF/versions/9/module-info.class</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>clean</id>
<phase>clean</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<delete dir="${project.basedir}/../hippo4j-agent" />
</target>
</configuration>
</execution>
<execution>
<id>package</id>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<mkdir dir="${project.basedir}/../hippo4j-agent" />
<copy file="${project.build.directory}/hippo4j-threadpool-agent.jar" tofile="${project.basedir}/../hippo4j-agent/hippo4j-threadpool-agent.jar" overwrite="true" />
<mkdir dir="${project.basedir}/../hippo4j-agent/config" />
<mkdir dir="${project.basedir}/../hippo4j-agent/logs" />
<copydir src="${project.basedir}/../config" dest="${project.basedir}/../hippo4j-agent/config" forceoverwrite="true" />
<copydir src="${project.basedir}/../dist-material" dest="${project.basedir}/../hippo4j-agent" />
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,263 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.bootstrap;
import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException;
import cn.hippo4j.agent.core.boot.ServiceManager;
import cn.hippo4j.agent.core.conf.Config;
import cn.hippo4j.agent.core.conf.SnifferConfigInitializer;
import cn.hippo4j.agent.core.jvm.LoadedLibraryCollector;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogManager;
import cn.hippo4j.agent.core.plugin.AbstractClassEnhancePluginDefine;
import cn.hippo4j.agent.core.plugin.EnhanceContext;
import cn.hippo4j.agent.core.plugin.InstrumentDebuggingClass;
import cn.hippo4j.agent.core.plugin.PluginBootstrap;
import cn.hippo4j.agent.core.plugin.PluginException;
import cn.hippo4j.agent.core.plugin.PluginFinder;
import cn.hippo4j.agent.core.plugin.bootstrap.BootstrapInstrumentBoost;
import cn.hippo4j.agent.core.plugin.bytebuddy.CacheableTransformerDecorator;
import cn.hippo4j.agent.core.plugin.jdk9module.JDK9ModuleExporter;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.NamedElement;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static net.bytebuddy.matcher.ElementMatchers.nameContains;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.not;
/**
* Hippo4j Agent
*/
public class Hippo4jAgent {
private static ILog LOGGER = LogManager.getLogger(Hippo4jAgent.class);
/**
* Main entrance. Use byte-buddy transform to enhance all classes, which define in plugins.
*/
public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException {
final PluginFinder pluginFinder;
try {
SnifferConfigInitializer.initializeCoreConfig(agentArgs);
} catch (Exception e) {
// try to resolve a new logger, and use the new logger to write the error log here
LogManager.getLogger(Hippo4jAgent.class)
.error(e, "Hippo4j agent initialized failure. Shutting down.");
return;
} finally {
// refresh logger again after initialization finishes
LOGGER = LogManager.getLogger(Hippo4jAgent.class);
}
try {
pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins());
} catch (AgentPackageNotFoundException ape) {
LOGGER.error(ape, "Locate agent.jar failure. Shutting down.");
return;
} catch (Exception e) {
LOGGER.error(e, "Hippo4j agent initialized failure. Shutting down.");
return;
}
final ByteBuddy byteBuddy = new ByteBuddy().with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));
AgentBuilder agentBuilder = new AgentBuilder.Default(byteBuddy).ignore(
nameStartsWith("net.bytebuddy.")
.or(nameStartsWith("org.slf4j."))
.or(nameStartsWith("org.groovy."))
.or(nameContains("javassist"))
.or(nameContains(".asm."))
.or(nameContains(".reflectasm."))
.or(nameStartsWith("sun.reflect"))
.or(allHippo4jAgentExcludeToolkit())
.or(ElementMatchers.isSynthetic()));
JDK9ModuleExporter.EdgeClasses edgeClasses = new JDK9ModuleExporter.EdgeClasses();
try {
agentBuilder = BootstrapInstrumentBoost.inject(pluginFinder, instrumentation, agentBuilder, edgeClasses);
} catch (Exception e) {
LOGGER.error(e, "Hippo4j agent inject bootstrap instrumentation failure. Shutting down.");
return;
}
try {
agentBuilder = JDK9ModuleExporter.openReadEdge(instrumentation, agentBuilder, edgeClasses);
} catch (Exception e) {
LOGGER.error(e, "Hippo4j agent open read edge in JDK 9+ failure. Shutting down.");
return;
}
if (Config.Agent.IS_CACHE_ENHANCED_CLASS) {
try {
agentBuilder = agentBuilder.with(new CacheableTransformerDecorator(Config.Agent.CLASS_CACHE_MODE));
LOGGER.info("Hippo4j agent class cache [{}] activated.", Config.Agent.CLASS_CACHE_MODE);
} catch (Exception e) {
LOGGER.error(e, "Hippo4j agent can't active class cache.");
}
}
agentBuilder.type(pluginFinder.buildMatch())
.transform(new Transformer(pluginFinder))
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(new RedefinitionListener())
.with(new Listener())
.installOn(instrumentation);
PluginFinder.pluginInitCompleted();
try {
ServiceManager.INSTANCE.boot();
} catch (Exception e) {
LOGGER.error(e, "Hippo4j agent boot failure.");
}
try {
Class.forName("java.util.concurrent.ThreadPoolExecutor");
} catch (ClassNotFoundException e) {
LOGGER.error(e, "Hippo4j agent boot failure.");
}
Runtime.getRuntime()
.addShutdownHook(new Thread(ServiceManager.INSTANCE::shutdown, "hippo4j service shutdown thread"));
}
/**
* transformer
*/
private static class Transformer implements AgentBuilder.Transformer {
private PluginFinder pluginFinder;
Transformer(PluginFinder pluginFinder) {
this.pluginFinder = pluginFinder;
}
@Override
public DynamicType.Builder<?> transform(final DynamicType.Builder<?> builder,
final TypeDescription typeDescription,
final ClassLoader classLoader,
final JavaModule javaModule,
final ProtectionDomain protectionDomain) {
LoadedLibraryCollector.registerURLClassLoader(classLoader);
List<AbstractClassEnhancePluginDefine> pluginDefines = pluginFinder.find(typeDescription);
if (pluginDefines.size() > 0) {
DynamicType.Builder<?> newBuilder = builder;
EnhanceContext context = new EnhanceContext();
for (AbstractClassEnhancePluginDefine define : pluginDefines) {
DynamicType.Builder<?> possibleNewBuilder = define.define(
typeDescription, newBuilder, classLoader, context);
if (possibleNewBuilder != null) {
newBuilder = possibleNewBuilder;
}
}
if (context.isEnhanced()) {
LOGGER.debug("Finish the prepare stage for {}.", typeDescription.getName());
}
return newBuilder;
}
LOGGER.debug("Matched class {}, but ignore by finding mechanism.", typeDescription.getTypeName());
return builder;
}
}
private static ElementMatcher.Junction<NamedElement> allHippo4jAgentExcludeToolkit() {
return nameStartsWith("cn.hippo4j").and(not(nameStartsWith("cn.hippo4j.agent.toolkit.")));
}
/**
* listener
*/
private static class Listener implements AgentBuilder.Listener {
@Override
public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
}
@Override
public void onTransformation(final TypeDescription typeDescription,
final ClassLoader classLoader,
final JavaModule module,
final boolean loaded,
final DynamicType dynamicType) {
if (LOGGER.isDebugEnable()) {
LOGGER.debug("On Transformation class {}.", typeDescription.getName());
}
InstrumentDebuggingClass.INSTANCE.log(dynamicType);
}
@Override
public void onIgnored(final TypeDescription typeDescription,
final ClassLoader classLoader,
final JavaModule module,
final boolean loaded) {
}
@Override
public void onError(final String typeName,
final ClassLoader classLoader,
final JavaModule module,
final boolean loaded,
final Throwable throwable) {
LOGGER.error("Enhance class " + typeName + " error.", throwable);
}
@Override
public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
}
}
/**
* redefinition listener
*/
private static class RedefinitionListener implements AgentBuilder.RedefinitionStrategy.Listener {
@Override
public void onBatch(int index, List<Class<?>> batch, List<Class<?>> types) {
/* do nothing */
}
@Override
public Iterable<? extends List<Class<?>>> onError(int index, List<Class<?>> batch, Throwable throwable, List<Class<?>> types) {
LOGGER.error(throwable, "index={}, batch={}, types={}", index, batch, types);
return Collections.emptyList();
}
@Override
public void onComplete(int amount, List<Class<?>> types, Map<List<Class<?>>, Throwable> failures) {
/* do nothing */
}
}
}

@ -0,0 +1,172 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-agent</artifactId>
<version>${revision}</version>
</parent>
<artifactId>hippo4j-agent-core</artifactId>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<netty-tcnative-boringssl-static.version>2.0.7.Final</netty-tcnative-boringssl-static.version>
<os-maven-plugin.version>1.4.1.Final</os-maven-plugin.version>
<shade.com.google.source>com.google</shade.com.google.source>
<shade.com.google.target>${shade.package}.${shade.com.google.source}</shade.com.google.target>
<shade.io.grpc.source>io.grpc</shade.io.grpc.source>
<shade.io.grpc.target>${shade.package}.${shade.io.grpc.source}</shade.io.grpc.target>
<shade.io.netty.source>io.netty</shade.io.netty.source>
<shade.io.netty.target>${shade.package}.${shade.io.netty.source}</shade.io.netty.target>
<shade.io.opencensus.source>io.opencensus</shade.io.opencensus.source>
<shade.io.opencensus.target>${shade.package}.${shade.io.opencensus.source}</shade.io.opencensus.target>
<shade.io.perfmark.source>io.perfmark</shade.io.perfmark.source>
<shade.io.perfmark.target>${shade.package}.${shade.io.perfmark.source}</shade.io.perfmark.target>
<shade.org.slf4j.source>org.slf4j</shade.org.slf4j.source>
<shade.org.slf4j.target>${shade.package}.${shade.org.slf4j.source}</shade.org.slf4j.target>
<ststem-rules.version>1.18.0</ststem-rules.version>
</properties>
<dependencies>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>jackson-annotations</artifactId>
<groupId>com.fasterxml.jackson.core</groupId>
</exclusion>
<exclusion>
<artifactId>jackson-core</artifactId>
<groupId>com.fasterxml.jackson.core</groupId>
</exclusion>
<exclusion>
<artifactId>jackson-databind</artifactId>
<groupId>com.fasterxml.jackson.core</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-threadpool-infra-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-threadpool-dynamic-mode-config</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-threadpool-kernel-monitor</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>${os-maven-plugin.version}</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>detect</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<excludes>
<exclude>net.bytebuddy:byte-buddy:jar:</exclude>
<exclude>com.google.errorprone:error_prone_annotations:jar:</exclude>
<exclude>com.google.code.findbugs:jsr305:jar:</exclude>
<exclude>com.google.android:annotations:jar:</exclude>
<exclude>com.google.api.grpc:proto-google-common-protos:jar:</exclude>
<exclude>org.checkerframework:checker-compat-qual:jar:</exclude>
<exclude>org.codehaus.mojo:animal-sniffer-annotations:jar:</exclude>
</excludes>
</artifactSet>
<relocations>
<relocation>
<pattern>${shade.com.google.source}</pattern>
<shadedPattern>${shade.com.google.target}</shadedPattern>
</relocation>
<relocation>
<pattern>${shade.io.grpc.source}</pattern>
<shadedPattern>${shade.io.grpc.target}</shadedPattern>
</relocation>
<relocation>
<pattern>${shade.io.netty.source}</pattern>
<shadedPattern>${shade.io.netty.target}</shadedPattern>
</relocation>
<relocation>
<pattern>${shade.io.opencensus.source}</pattern>
<shadedPattern>${shade.io.opencensus.target}</shadedPattern>
</relocation>
<relocation>
<pattern>${shade.io.perfmark.source}</pattern>
<shadedPattern>${shade.io.perfmark.target}</shadedPattern>
</relocation>
<relocation>
<pattern>${shade.org.slf4j.source}</pattern>
<shadedPattern>${shade.org.slf4j.target}</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
<artifact>com.google.protobuf:protobuf-java</artifact>
<excludes>
<exclude>google/protobuf/*.proto</exclude>
<exclude>google/protobuf/compiler/*.proto</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.base64;
import java.nio.charset.StandardCharsets;
/**
* A wrapper of {@link java.util.Base64} with convenient conversion methods between {@code byte[]} and {@code String}
*/
public final class Base64 {
private static final java.util.Base64.Decoder DECODER = java.util.Base64.getDecoder();
private static final java.util.Base64.Encoder ENCODER = java.util.Base64.getEncoder();
private Base64() {
}
public static String decode2UTFString(String in) {
return new String(DECODER.decode(in), StandardCharsets.UTF_8);
}
public static String encode(String text) {
return ENCODER.encodeToString(text.getBytes(StandardCharsets.UTF_8));
}
}

@ -0,0 +1,25 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.boot;
public class AgentPackageNotFoundException extends Exception {
public AgentPackageNotFoundException(String message) {
super(message);
}
}

@ -0,0 +1,85 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.boot;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogManager;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
/**
* AgentPackagePath is a flag and finder to locate the Hippo4j agent.jar. It gets the absolute path of the agent jar.
* The path is the required metadata for agent core looking up the plugins and toolkit activations. If the lookup
* mechanism fails, the agent will exit directly.
*/
public class AgentPackagePath {
private static final ILog LOGGER = LogManager.getLogger(AgentPackagePath.class);
private static File AGENT_PACKAGE_PATH;
public static File getPath() throws AgentPackageNotFoundException {
if (AGENT_PACKAGE_PATH == null) {
AGENT_PACKAGE_PATH = findPath();
}
return AGENT_PACKAGE_PATH;
}
public static boolean isPathFound() {
return AGENT_PACKAGE_PATH != null;
}
private static File findPath() throws AgentPackageNotFoundException {
String classResourcePath = AgentPackagePath.class.getName().replaceAll("\\.", "/") + ".class";
URL resource = ClassLoader.getSystemClassLoader().getResource(classResourcePath);
if (resource != null) {
String urlString = resource.toString();
LOGGER.debug("The beacon class location is {}.", urlString);
int insidePathIndex = urlString.indexOf('!');
boolean isInJar = insidePathIndex > -1;
if (isInJar) {
urlString = urlString.substring(urlString.indexOf("file:"), insidePathIndex);
File agentJarFile = null;
try {
agentJarFile = new File(new URL(urlString).toURI());
} catch (MalformedURLException | URISyntaxException e) {
LOGGER.error(e, "Can not locate agent jar file by url:" + urlString);
}
if (agentJarFile.exists()) {
return agentJarFile.getParentFile();
}
} else {
int prefixLength = "file:".length();
String classLocation = urlString.substring(
prefixLength, urlString.length() - classResourcePath.length());
return new File(classLocation);
}
}
LOGGER.error("Can not locate agent jar file.");
throw new AgentPackageNotFoundException("Can not locate agent jar file.");
}
}

@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.boot;
/**
* The <code>BootService</code> is an interface to all remote, which need to boot when plugin mechanism begins to work.
* {@link #boot()} will be called when <code>BootService</code> start up.
*/
public interface BootService {
void prepare() throws Throwable;
void boot() throws Throwable;
void onComplete() throws Throwable;
void shutdown() throws Throwable;
/**
* {@code BootService}s with higher priorities will be started earlier, and shut down later than those {@code BootService}s with lower priorities.
*
* @return the priority of this {@code BootService}.
*/
default int priority() {
return 0;
}
}

@ -0,0 +1,28 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.boot;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DefaultImplementor {
}

@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.boot;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
public class DefaultNamedThreadFactory implements ThreadFactory {
private static final AtomicInteger BOOT_SERVICE_SEQ = new AtomicInteger(0);
private final AtomicInteger threadSeq = new AtomicInteger(0);
private final String namePrefix;
public DefaultNamedThreadFactory(String name) {
namePrefix = "Hippo4jAgent-" + BOOT_SERVICE_SEQ.incrementAndGet() + "-" + name + "-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, namePrefix + threadSeq.getAndIncrement());
t.setDaemon(true);
return t;
}
}

@ -0,0 +1,30 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.boot;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface OverrideImplementor {
Class<? extends BootService> value();
}

@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.boot;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* ConfigInitializationService provides the config class which should host all parameters originally from agent setup.
* {@link cn.hippo4j.agent.core.conf.Config} provides the core level config, all plugins could implement
* this interface to have the same capability about initializing config from agent.config, system properties and system
* environment variables.
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PluginConfig {
/**
* @return Class as the root to do config initialization.
*/
Class<?> root();
}

@ -0,0 +1,25 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.boot;
public class ServiceConflictException extends RuntimeException {
public ServiceConflictException(String message) {
super(message);
}
}

@ -0,0 +1,149 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.boot;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogManager;
import cn.hippo4j.agent.core.plugin.loader.AgentClassLoader;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
/**
* The <code>ServiceManager</code> bases on {@link ServiceLoader}, load all {@link BootService} implementations.
*/
public enum ServiceManager {
INSTANCE;
private static final ILog LOGGER = LogManager.getLogger(ServiceManager.class);
private Map<Class, BootService> bootedServices = Collections.emptyMap();
public void boot() {
bootedServices = loadAllServices();
prepare();
startup();
onComplete();
}
public void shutdown() {
bootedServices.values().stream().sorted(Comparator.comparingInt(BootService::priority).reversed()).forEach(service -> {
try {
service.shutdown();
} catch (Throwable e) {
LOGGER.error(e, "ServiceManager try to shutdown [{}] fail.", service.getClass().getName());
}
});
}
private Map<Class, BootService> loadAllServices() {
Map<Class, BootService> bootedServices = new LinkedHashMap<>();
List<BootService> allServices = new LinkedList<>();
load(allServices);
for (final BootService bootService : allServices) {
Class<? extends BootService> bootServiceClass = bootService.getClass();
boolean isDefaultImplementor = bootServiceClass.isAnnotationPresent(DefaultImplementor.class);
if (isDefaultImplementor) {
if (!bootedServices.containsKey(bootServiceClass)) {
bootedServices.put(bootServiceClass, bootService);
} else {
// ignore the default service
}
} else {
OverrideImplementor overrideImplementor = bootServiceClass.getAnnotation(OverrideImplementor.class);
if (overrideImplementor == null) {
if (!bootedServices.containsKey(bootServiceClass)) {
bootedServices.put(bootServiceClass, bootService);
} else {
throw new ServiceConflictException("Duplicate service define for :" + bootServiceClass);
}
} else {
Class<? extends BootService> targetService = overrideImplementor.value();
if (bootedServices.containsKey(targetService)) {
boolean presentDefault = bootedServices.get(targetService)
.getClass()
.isAnnotationPresent(DefaultImplementor.class);
if (presentDefault) {
bootedServices.put(targetService, bootService);
} else {
throw new ServiceConflictException(
"Service " + bootServiceClass + " overrides conflict, " + "exist more than one service want to override :" + targetService);
}
} else {
bootedServices.put(targetService, bootService);
}
}
}
}
return bootedServices;
}
private void prepare() {
bootedServices.values().stream().sorted(Comparator.comparingInt(BootService::priority)).forEach(service -> {
try {
service.prepare();
} catch (Throwable e) {
LOGGER.error(e, "ServiceManager try to pre-start [{}] fail.", service.getClass().getName());
}
});
}
private void startup() {
bootedServices.values().stream().sorted(Comparator.comparingInt(BootService::priority)).forEach(service -> {
try {
service.boot();
} catch (Throwable e) {
LOGGER.error(e, "ServiceManager try to start [{}] fail.", service.getClass().getName());
}
});
}
private void onComplete() {
for (BootService service : bootedServices.values()) {
try {
service.onComplete();
} catch (Throwable e) {
LOGGER.error(e, "Service [{}] AfterBoot process fails.", service.getClass().getName());
}
}
}
/**
* Find a {@link BootService} implementation, which is already started.
*
* @param serviceClass class name.
* @param <T> {@link BootService} implementation class.
* @return {@link BootService} instance
*/
public <T extends BootService> T findService(Class<T> serviceClass) {
return (T) bootedServices.get(serviceClass);
}
void load(List<BootService> allServices) {
for (final BootService bootService : ServiceLoader.load(BootService.class, AgentClassLoader.getDefault())) {
allServices.add(bootService);
}
}
}

@ -0,0 +1,87 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.boot;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogManager;
import cn.hippo4j.agent.core.util.ConfigInitializer;
import java.util.Collections;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
public class SpringBootConfigInitializer {
private static final ILog LOG = LogManager.getLogger(SpringBootConfigInitializer.class);
private static final Set<Class<?>> SPRING_BOOT_CONFIG_LIST = Collections.synchronizedSet(new HashSet<>());
private static final long MAX_CACHE_TIME = 30L * 60L * 1000L; // half an hour.
private static final int MAX_CACHE_SIZE = 1000;
private static long PROPERTIES_LOAD_TIME;
public static Properties SPRING_PROPERTIES = null;
private SpringBootConfigInitializer() {
}
public static boolean isSpringPropertiesEmpty() {
return SPRING_PROPERTIES == null || SPRING_PROPERTIES.isEmpty();
}
public static synchronized void initializeConfig(SpringBootConfigNode springBootConfig) {
if (SPRING_PROPERTIES != null) {
try {
LOG.info("initialize Spring Config Class {}.", springBootConfig.root());
ConfigInitializer.initialize(SPRING_PROPERTIES, springBootConfig.root(), true);
} catch (Throwable e) {
LOG.error(e, "Failed to set the agent settings {} to Config={} ", SPRING_PROPERTIES, springBootConfig.root());
}
}
boolean isStarting = PROPERTIES_LOAD_TIME == 0L;
boolean overtime = System.currentTimeMillis() - PROPERTIES_LOAD_TIME > MAX_CACHE_TIME;
boolean oversize = SPRING_BOOT_CONFIG_LIST.size() > MAX_CACHE_SIZE;
// avoid memory leak.
if (isStarting || (!oversize && !overtime)) {
SPRING_BOOT_CONFIG_LIST.add(springBootConfig.root());
} else {
LOG.warn("spirng Config Class is skipped {}.", springBootConfig.root());
}
}
public static synchronized void setSpringProperties(Properties properties) {
if (properties != null && (SPRING_PROPERTIES == null || properties.size() > SPRING_PROPERTIES.size())) {
LOG.info("set Spring Config Properties before : {}.", SPRING_PROPERTIES);
SPRING_PROPERTIES = properties;
LOG.info("set Spring Config Properties after : {}.", SPRING_PROPERTIES);
PROPERTIES_LOAD_TIME = System.currentTimeMillis();
}
for (Class<?> clazz : SPRING_BOOT_CONFIG_LIST) {
try {
LOG.info("initialize Spring Config Class in loop {}.", clazz);
ConfigInitializer.initialize(SPRING_PROPERTIES, clazz);
} catch (Throwable e) {
LOG.error(e, "Failed to set the agent Config={} from settings {}", clazz, properties);
}
}
}
}

@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.boot;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SpringBootConfigNode {
/**
* @return Class as the root to do config initialization.
*/
Class<?> root();
}

@ -0,0 +1,195 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.conf;
import cn.hippo4j.agent.core.logging.core.LogLevel;
import cn.hippo4j.agent.core.logging.core.LogOutput;
import cn.hippo4j.agent.core.logging.core.ResolverType;
import cn.hippo4j.agent.core.logging.core.WriterFactory;
import cn.hippo4j.agent.core.plugin.bytebuddy.ClassCacheMode;
import cn.hippo4j.agent.core.util.Length;
import java.util.Arrays;
import java.util.List;
/**
* This is the core config in hippo4j agent.
*/
public class Config {
public static class Agent {
/**
* Namespace represents a subnet, such as kubernetes namespace, or 172.10.*.*.
*
* @since 8.10.0 namespace would be added as {@link #SERVICE_NAME} suffix.
*
* Removed namespace isolating headers in cross process propagation. The HEADER name was
* `HeaderName:Namespace`.
*/
@Length(20)
public static String NAMESPACE = "";
/**
* Service name is showed on the UI. Suggestion: set a unique name for each service, service instance nodes
* share the same code
*
* @since 8.10.0 ${service name} = [${group name}::]${logic name}|${NAMESPACE}|${CLUSTER}
*
* The group name, namespace and cluster are optional. Once they are all blank, service name would be the final
* name.
*/
@Length(50)
public static String SERVICE_NAME = "";
/**
* Cluster defines the physical cluster in a data center or same network segment. In one cluster, IP address
* should be unique identify.
*
* The cluster name would be
*
* 1. Add as {@link #SERVICE_NAME} suffix.
*
* 2. Add as exit span's peer, ${CLUSTER} / original peer
*
* 3. Cross Process Propagation Header's value addressUsedAtClient[index=8] (Target address of this request used
* on the client end).
*
* @since 8.10.0
*/
@Length(20)
public static String CLUSTER = "";
/**
* If true, Hippo4j agent will save all instrumented classes files in `/debugging` folder. Hippo4j team
* may ask for these files in order to resolve compatible problem.
*/
public static boolean IS_OPEN_DEBUGGING_CLASS = false;
/**
* If true, Hippo4j agent will cache all instrumented classes to memory or disk files (decided by class cache
* mode), allow other javaagent to enhance those classes that enhanced by Hippo4j agent.
*/
public static boolean IS_CACHE_ENHANCED_CLASS = false;
/**
* The instrumented classes cache mode: MEMORY or FILE MEMORY: cache class bytes to memory, if instrumented
* classes is too many or too large, it may take up more memory FILE: cache class bytes in `/class-cache`
* folder, automatically clean up cached class files when the application exits
*/
public static ClassCacheMode CLASS_CACHE_MODE = ClassCacheMode.MEMORY;
}
public static class Logging {
/**
* Log file name.
*/
public static String FILE_NAME = "hippo4j-api.log";
/**
* Log files directory. Default is blank string, means, use "{theHippo4jAgentJarDir}/logs " to output logs.
* {theHippo4jAgentJarDir} is the directory where the hippo4j agent jar file is located.
* <p>
* Ref to {@link WriterFactory#getLogWriter()}
*/
public static String DIR = "";
/**
* The max size of log file. If the size is bigger than this, archive the current file, and write into a new
* file.
*/
public static int MAX_FILE_SIZE = 300 * 1024 * 1024;
/**
* The max history log files. When rollover happened, if log files exceed this number, then the oldest file will
* be delete. Negative or zero means off, by default.
*/
public static int MAX_HISTORY_FILES = -1;
/**
* The log level. Default is debug.
*/
public static LogLevel LEVEL = LogLevel.DEBUG;
/**
* The log output. Default is FILE.
*/
public static LogOutput OUTPUT = LogOutput.FILE;
/**
* The log resolver type. Default is PATTERN which will create PatternLogResolver later.
*/
public static ResolverType RESOLVER = ResolverType.PATTERN;
/**
* The log patten. Default is "%level %timestamp %thread %class : %msg %throwable". Each conversion specifiers
* starts with a percent sign '%' and fis followed by conversion word. There are some default conversion
* specifiers: %thread = ThreadName %level = LogLevel {@link LogLevel} %timestamp = The now() who format is
* 'yyyy-MM-dd HH:mm:ss:SSS' %class = SimpleName of TargetClass %msg = Message of user input %throwable =
* Throwable of user input %agent_name = ServiceName of Agent {@link Agent#SERVICE_NAME}
*
* @see cn.hippo4j.agent.core.logging.core.PatternLogger#DEFAULT_CONVERTER_MAP
*/
public static String PATTERN = "%level %timestamp %thread %class : %msg %throwable";
}
public static class Plugin {
/**
* Control the length of the peer field.
*/
public static int PEER_MAX_LENGTH = 200;
/**
* Exclude activated plugins
*/
public static String EXCLUDE_PLUGINS = "";
/**
* Mount the folders of the plugins. The folder path is relative to agent.jar.
*/
public static List<String> MOUNT = Arrays.asList("plugins", "activations");
public static class ThreadPool {
public static List<String> EXCLUDE_PACKAGE_PREFIX = Arrays.asList(
"java", "sun", "okhttp3", "retrofit2", "reactor",
"org.apache", "io.netty", "org.springframework", "com.ctrip", "com.google",
"io.undertow", "org.xnio", "org.jboss", "com.zaxxer", "org.redisson", "com.alibaba",
"com.netflix", "com.mysql", "rx.internal", "io.shardingjdbc", "org.drools", "org.elasticsearch",
"ch.qos.logback", "net.sf.ehcache");
}
public static class Apollo {
public static class App {
public static String ID;
}
public static String META;
public static class BootStrap {
public static boolean ENABLED = false;
public static List<String> NAMESPACES;
}
}
}
}

@ -0,0 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.conf;
public class ConfigNotFoundException extends Exception {
public ConfigNotFoundException(String message, Throwable cause) {
super(message, cause);
}
public ConfigNotFoundException(String message) {
super(message);
}
}

@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.conf;
public class Constants {
public static String PATH_SEPARATOR = System.getProperty("file.separator", "/");
public static String LINE_SEPARATOR = System.getProperty("line.separator", "\n");
public static String EMPTY_STRING = "";
public static char SERVICE_NAME_PART_CONNECTOR = '|';
// The name of the layer that represents agent-installed services,
// which is defined at
// https://github.com/apache/skywalking/blob/85ce1645be53e46286f36c0ea206c60db2d1a716/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/Layer.java#L30
public static String EVENT_LAYER_NAME = "GENERAL";
public static int NULL_VALUE = 0;
public static String SPRING_BOOT_CONFIG_PREFIX = "spring.dynamic.thread-pool";
}

@ -0,0 +1,27 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.conf;
public class RuntimeContextConfiguration {
public static String[] NEED_PROPAGATE_CONTEXT_KEY = new String[]{
"SW_REQUEST",
"SW_RESPONSE",
"SW_WEBFLUX_REQUEST_KEY"
};
}

@ -0,0 +1,234 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.conf;
import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException;
import cn.hippo4j.agent.core.boot.AgentPackagePath;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogManager;
import cn.hippo4j.agent.core.logging.core.JsonLogResolver;
import cn.hippo4j.agent.core.logging.core.PatternLogResolver;
import cn.hippo4j.agent.core.util.ConfigInitializer;
import cn.hippo4j.agent.core.util.PropertyPlaceholderHelper;
import cn.hippo4j.agent.core.util.StringUtil;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import static cn.hippo4j.agent.core.conf.Constants.SERVICE_NAME_PART_CONNECTOR;
/**
* The <code>SnifferConfigInitializer</code> initializes all configs in several way.
*/
public class SnifferConfigInitializer {
private static ILog LOGGER = LogManager.getLogger(SnifferConfigInitializer.class);
private static final String SPECIFIED_CONFIG_PATH = "hippo4j_config";
private static final String DEFAULT_CONFIG_FILE_NAME = "/config/agent.config";
private static final String ENV_KEY_PREFIX = "hippo4j.";
private static Properties AGENT_SETTINGS;
private static boolean IS_INIT_COMPLETED = false;
/**
* If the specified agent config path is set, the agent will try to locate the specified agent config. If the
* specified agent config path is not set , the agent will try to locate `agent.config`, which should be in the
* /config directory of agent package.
* <p>
* Also try to override the config by system.properties. All the keys in this place should start with {@link
* #ENV_KEY_PREFIX}. e.g. in env `hippo4j.agent.service_name=yourAppName` to override `agent.service_name` in
* config file.
* <p>
* At the end, `agent.service_name` and `collector.servers` must not be blank.
*/
public static void initializeCoreConfig(String agentOptions) {
AGENT_SETTINGS = new Properties();
try (final InputStreamReader configFileStream = loadConfig()) {
AGENT_SETTINGS.load(configFileStream);
for (String key : AGENT_SETTINGS.stringPropertyNames()) {
String value = (String) AGENT_SETTINGS.get(key);
AGENT_SETTINGS.put(key, PropertyPlaceholderHelper.INSTANCE.replacePlaceholders(value, AGENT_SETTINGS));
}
} catch (Exception e) {
LOGGER.error(e, "Failed to read the config file, hippo4j is going to run in default config.");
}
try {
overrideConfigBySystemProp();
} catch (Exception e) {
LOGGER.error(e, "Failed to read the system properties.");
}
agentOptions = StringUtil.trim(agentOptions, ',');
if (!StringUtil.isEmpty(agentOptions)) {
try {
agentOptions = agentOptions.trim();
LOGGER.info("Agent options is {}.", agentOptions);
overrideConfigByAgentOptions(agentOptions);
} catch (Exception e) {
LOGGER.error(e, "Failed to parse the agent options, val is {}.", agentOptions);
}
}
initializeConfig(Config.class);
// reconfigure logger after config initialization
configureLogger();
LOGGER = LogManager.getLogger(SnifferConfigInitializer.class);
if (StringUtil.isEmpty(Config.Agent.SERVICE_NAME)) {
throw new ExceptionInInitializerError("`agent.service_name` is missing.");
} else {
if (StringUtil.isNotEmpty(Config.Agent.NAMESPACE) || StringUtil.isNotEmpty(Config.Agent.CLUSTER)) {
Config.Agent.SERVICE_NAME = StringUtil.join(
SERVICE_NAME_PART_CONNECTOR,
Config.Agent.SERVICE_NAME,
Config.Agent.NAMESPACE,
Config.Agent.CLUSTER);
}
}
// if (StringUtil.isEmpty(Config.Collector.BACKEND_SERVICE)) {
// throw new ExceptionInInitializerError("`collector.backend_service` is missing.");
// }
if (Config.Plugin.PEER_MAX_LENGTH <= 3) {
LOGGER.warn(
"PEER_MAX_LENGTH configuration:{} error, the default value of 200 will be used.",
Config.Plugin.PEER_MAX_LENGTH);
Config.Plugin.PEER_MAX_LENGTH = 200;
}
IS_INIT_COMPLETED = true;
}
/**
* Initialize field values of any given config class.
*
* @param configClass to host the settings for code access.
*/
public static void initializeConfig(Class configClass) {
if (AGENT_SETTINGS == null) {
LOGGER.error("Plugin configs have to be initialized after core config initialization.");
return;
}
try {
ConfigInitializer.initialize(AGENT_SETTINGS, configClass);
} catch (IllegalAccessException e) {
LOGGER.error(e,
"Failed to set the agent settings {}"
+ " to Config={} ",
AGENT_SETTINGS, configClass);
}
}
private static void overrideConfigByAgentOptions(String agentOptions) throws IllegalArgumentException {
for (List<String> terms : parseAgentOptions(agentOptions)) {
if (terms.size() != 2) {
throw new IllegalArgumentException("[" + terms + "] is not a key-value pair.");
}
AGENT_SETTINGS.put(terms.get(0), terms.get(1));
}
}
private static List<List<String>> parseAgentOptions(String agentOptions) {
List<List<String>> options = new ArrayList<>();
List<String> terms = new ArrayList<>();
boolean isInQuotes = false;
StringBuilder currentTerm = new StringBuilder();
for (char c : agentOptions.toCharArray()) {
if (c == '\'' || c == '"') {
isInQuotes = !isInQuotes;
} else if (c == '=' && !isInQuotes) { // key-value pair uses '=' as separator
terms.add(currentTerm.toString());
currentTerm = new StringBuilder();
} else if (c == ',' && !isInQuotes) { // multiple options use ',' as separator
terms.add(currentTerm.toString());
currentTerm = new StringBuilder();
options.add(terms);
terms = new ArrayList<>();
} else {
currentTerm.append(c);
}
}
// add the last term and option without separator
terms.add(currentTerm.toString());
options.add(terms);
return options;
}
public static boolean isInitCompleted() {
return IS_INIT_COMPLETED;
}
/**
* Override the config by system properties. The property key must start with `hippo4j`, the result should be as
* same as in `agent.config`
* <p>
* such as: Property key of `agent.service_name` should be `hippo4j.agent.service_name`
*/
private static void overrideConfigBySystemProp() {
Properties systemProperties = System.getProperties();
for (final Map.Entry<Object, Object> prop : systemProperties.entrySet()) {
String key = prop.getKey().toString();
if (key.startsWith(ENV_KEY_PREFIX)) {
String realKey = key.substring(ENV_KEY_PREFIX.length());
AGENT_SETTINGS.put(realKey, prop.getValue());
}
}
}
/**
* Load the specified config file or default config file
*
* @return the config file {@link InputStream}, or null if not needEnhance.
*/
private static InputStreamReader loadConfig() throws AgentPackageNotFoundException, ConfigNotFoundException {
String specifiedConfigPath = System.getProperty(SPECIFIED_CONFIG_PATH);
File configFile = StringUtil.isEmpty(specifiedConfigPath) ? new File(
AgentPackagePath.getPath(), DEFAULT_CONFIG_FILE_NAME) : new File(specifiedConfigPath);
if (configFile.exists() && configFile.isFile()) {
try {
LOGGER.info("Config file found in {}.", configFile);
return new InputStreamReader(new FileInputStream(configFile), StandardCharsets.UTF_8);
} catch (FileNotFoundException e) {
throw new ConfigNotFoundException("Failed to load agent.config", e);
}
}
throw new ConfigNotFoundException("Failed to load agent.config.");
}
static void configureLogger() {
switch (Config.Logging.RESOLVER) {
case JSON:
LogManager.setLogResolver(new JsonLogResolver());
break;
case PATTERN:
default:
LogManager.setLogResolver(new PatternLogResolver());
}
}
}

@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.conf.dynamic;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
public abstract class AgentConfigChangeWatcher {
// Config key, should match KEY in the Table of Agent Configuration Properties.
private final String propertyKey;
public AgentConfigChangeWatcher(String propertyKey) {
this.propertyKey = propertyKey;
}
/**
* Notify the watcher, the new value received.
*
* @param value of new.
*/
public abstract void notify(ConfigChangeEvent value);
/**
* @return current value of current config.
*/
public abstract String value();
@Override
public String toString() {
return "AgentConfigChangeWatcher{"
+ "propertyKey='" + propertyKey + '\''
+ '}';
}
@Getter
@RequiredArgsConstructor
public static class ConfigChangeEvent {
private final String newValue;
private final EventType eventType;
}
public enum EventType {
ADD, MODIFY, DELETE
}
}

@ -0,0 +1,132 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.jvm;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogManager;
import cn.hippo4j.agent.core.util.CollectionUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.File;
import java.lang.management.ManagementFactory;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class LoadedLibraryCollector {
private static final ILog LOGGER = LogManager.getLogger(LoadedLibraryCollector.class);
private static final String JAR_SEPARATOR = "!";
private static Set<ClassLoader> CURRENT_URL_CLASSLOADER_SET = new HashSet<>();
private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();
/**
* Prevent OOM in special scenes
*/
private static int CURRENT_URL_CLASSLOADER_SET_MAX_SIZE = 50;
public static void registerURLClassLoader(ClassLoader classLoader) {
if (CURRENT_URL_CLASSLOADER_SET.size() < CURRENT_URL_CLASSLOADER_SET_MAX_SIZE && classLoader instanceof URLClassLoader) {
CURRENT_URL_CLASSLOADER_SET.add(classLoader);
}
}
private static String getVmStartTime() {
long startTime = ManagementFactory.getRuntimeMXBean().getStartTime();
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(startTime));
}
private static List<String> getVmArgs() {
List<String> vmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
List<String> sortedVmArgs = new ArrayList<>(vmArgs);
Collections.sort(sortedVmArgs);
return sortedVmArgs;
}
private static List<String> getLibJarNames() {
List<URL> classLoaderUrls = loadClassLoaderUrls();
return extractLibJarNamesFromURLs(classLoaderUrls);
}
private static List<URL> loadClassLoaderUrls() {
List<URL> classLoaderUrls = new ArrayList<>();
for (ClassLoader classLoader : CURRENT_URL_CLASSLOADER_SET) {
try {
URLClassLoader webappClassLoader = (URLClassLoader) classLoader;
URL[] urls = webappClassLoader.getURLs();
classLoaderUrls.addAll(Arrays.asList(urls));
} catch (Exception e) {
LOGGER.warn("Load classloader urls exception: {}", e.getMessage());
}
}
return classLoaderUrls;
}
private static List<String> extractLibJarNamesFromURLs(List<URL> urls) {
Set<String> libJarNames = new HashSet<>();
for (URL url : urls) {
try {
String libJarName = extractLibJarName(url);
if (libJarName.endsWith(".jar")) {
libJarNames.add(libJarName);
}
} catch (Exception e) {
LOGGER.warn("Extracting library name exception: {}", e.getMessage());
}
}
List<String> sortedLibJarNames = new ArrayList<>(libJarNames.size());
if (!CollectionUtil.isEmpty(libJarNames)) {
sortedLibJarNames.addAll(libJarNames);
Collections.sort(sortedLibJarNames);
}
return sortedLibJarNames;
}
private static String extractLibJarName(URL url) {
String protocol = url.getProtocol();
if (protocol.equals("file")) {
return extractNameFromFile(url.toString());
} else if (protocol.equals("jar")) {
return extractNameFromJar(url.toString());
} else {
return "";
}
}
private static String extractNameFromFile(String fileUri) {
int lastIndexOfSeparator = fileUri.lastIndexOf(File.separator);
if (lastIndexOfSeparator < 0) {
return fileUri;
} else {
return fileUri.substring(lastIndexOfSeparator + 1);
}
}
private static String extractNameFromJar(String jarUri) {
String uri = jarUri.substring(0, jarUri.lastIndexOf(JAR_SEPARATOR));
return extractNameFromFile(uri);
}
}

@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.api;
/**
* The Log interface. It's very easy to understand, like any other log-component. Do just like log4j or log4j2 does.
* <p>
*/
public interface ILog {
void info(String format);
void info(String format, Object... arguments);
void info(Throwable t, String format, Object... arguments);
void warn(String format, Object... arguments);
void warn(Throwable e, String format, Object... arguments);
void error(String format, Throwable e);
void error(Throwable e, String format, Object... arguments);
boolean isDebugEnable();
boolean isInfoEnable();
boolean isWarnEnable();
boolean isErrorEnable();
boolean isTraceEnabled();
void debug(String format);
void debug(String format, Object... arguments);
void debug(Throwable t, String format, Object... arguments);
void error(String format);
void trace(String format);
void trace(String format, Object... arguments);
void trace(Throwable t, String format, Object... arguments);
}

@ -0,0 +1,55 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.api;
import cn.hippo4j.agent.core.logging.core.PatternLogResolver;
/**
* LogManager is the {@link LogResolver} implementation manager. By using {@link LogResolver}, {@link
* LogManager#getLogger(Class)} returns a {@link ILog} implementation. This module use this class as the main entrance,
* and block the implementation detail about log-component. In different modules, like server or sniffer, it will use
* different implementations.
*
* <p> If no {@link LogResolver} is registered, return {@link NoopLogger#INSTANCE} to avoid
* {@link NullPointerException}. If {@link LogManager#setLogResolver(LogResolver)} is called twice, the second will
* override the first without any warning or exception.
*
* <p> Created by xin on 2016/11/10.
*/
public class LogManager {
private static LogResolver RESOLVER = new PatternLogResolver();
public static void setLogResolver(LogResolver resolver) {
LogManager.RESOLVER = resolver;
}
public static ILog getLogger(Class<?> clazz) {
if (RESOLVER == null) {
return NoopLogger.INSTANCE;
}
return LogManager.RESOLVER.getLogger(clazz);
}
public static ILog getLogger(String clazz) {
if (RESOLVER == null) {
return NoopLogger.INSTANCE;
}
return LogManager.RESOLVER.getLogger(clazz);
}
}

@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.api;
/**
* {@link LogResolver} just do only one thing: return the {@link ILog} implementation.
* <p>
*/
public interface LogResolver {
/**
* @param clazz the class is showed in log message.
* @return {@link ILog} implementation.
*/
ILog getLogger(Class<?> clazz);
/**
* @param clazz the class is showed in log message.
* @return {@link ILog} implementation.
*/
ILog getLogger(String clazz);
}

@ -0,0 +1,122 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.api;
/**
* No operation logger implementation. Just implement {@link ILog} interface, but do nothing.
* <p>
*/
public enum NoopLogger implements ILog {
INSTANCE;
@Override
public void info(String message) {
}
@Override
public void info(String format, Object... arguments) {
}
@Override
public void info(final Throwable t, final String format, final Object... arguments) {
}
@Override
public void warn(String format, Object... arguments) {
}
@Override
public void error(String format, Throwable e) {
}
@Override
public boolean isDebugEnable() {
return false;
}
@Override
public boolean isInfoEnable() {
return false;
}
@Override
public boolean isWarnEnable() {
return false;
}
@Override
public boolean isErrorEnable() {
return false;
}
@Override
public boolean isTraceEnabled() {
return false;
}
@Override
public void debug(String format) {
}
@Override
public void debug(String format, Object... arguments) {
}
@Override
public void debug(final Throwable t, final String format, final Object... arguments) {
}
@Override
public void error(String format) {
}
@Override
public void trace(final String format) {
}
@Override
public void trace(final String format, final Object... arguments) {
}
@Override
public void trace(final Throwable t, final String format, final Object... arguments) {
}
@Override
public void error(Throwable e, String format, Object... arguments) {
}
@Override
public void warn(Throwable e, String format, Object... arguments) {
}
}

@ -0,0 +1,222 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core;
import cn.hippo4j.agent.core.conf.Config;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.core.converters.AgentNameConverter;
import cn.hippo4j.agent.core.logging.core.converters.ClassConverter;
import cn.hippo4j.agent.core.logging.core.converters.DateConverter;
import cn.hippo4j.agent.core.logging.core.converters.LevelConverter;
import cn.hippo4j.agent.core.logging.core.converters.MessageConverter;
import cn.hippo4j.agent.core.logging.core.converters.ThreadConverter;
import cn.hippo4j.agent.core.logging.core.converters.ThrowableConverter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
/**
* An abstract class to simplify the real implementation of the loggers.
* It hold the class name of the logger, and is responsible for log level check,
* message interpolation, etc.
*/
public abstract class AbstractLogger implements ILog {
public static final Map<String, Class<? extends Converter>> DEFAULT_CONVERTER_MAP = new HashMap<>();
protected List<Converter> converters = new ArrayList<>();
static {
DEFAULT_CONVERTER_MAP.put("thread", ThreadConverter.class);
DEFAULT_CONVERTER_MAP.put("level", LevelConverter.class);
DEFAULT_CONVERTER_MAP.put("agent_name", AgentNameConverter.class);
DEFAULT_CONVERTER_MAP.put("timestamp", DateConverter.class);
DEFAULT_CONVERTER_MAP.put("msg", MessageConverter.class);
DEFAULT_CONVERTER_MAP.put("throwable", ThrowableConverter.class);
DEFAULT_CONVERTER_MAP.put("class", ClassConverter.class);
}
protected final String targetClass;
public AbstractLogger(String targetClass) {
this.targetClass = targetClass;
}
@Override
public void info(String message) {
if (this.isInfoEnable()) {
this.logger(LogLevel.INFO, message, null);
}
}
@Override
public void info(String message, Object... objects) {
if (this.isInfoEnable()) {
this.logger(LogLevel.INFO, replaceParam(message, objects), null);
}
}
@Override
public void info(final Throwable throwable, final String message, final Object... objects) {
if (this.isInfoEnable()) {
this.logger(LogLevel.INFO, replaceParam(message, objects), throwable);
}
}
@Override
public void warn(String message, Object... objects) {
if (this.isWarnEnable()) {
this.logger(LogLevel.WARN, replaceParam(message, objects), null);
}
}
@Override
public void warn(Throwable throwable, String message, Object... objects) {
if (this.isWarnEnable()) {
this.logger(LogLevel.WARN, replaceParam(message, objects), throwable);
}
}
@Override
public void error(String message, Throwable throwable) {
if (this.isErrorEnable()) {
this.logger(LogLevel.ERROR, message, throwable);
}
}
@Override
public void error(Throwable throwable, String message, Object... objects) {
if (this.isErrorEnable()) {
this.logger(LogLevel.ERROR, replaceParam(message, objects), throwable);
}
}
@Override
public void error(String message) {
if (this.isErrorEnable()) {
this.logger(LogLevel.ERROR, message, null);
}
}
@Override
public void debug(String message) {
if (this.isDebugEnable()) {
this.logger(LogLevel.DEBUG, message, null);
}
}
@Override
public void debug(String message, Object... objects) {
if (this.isDebugEnable()) {
this.logger(LogLevel.DEBUG, replaceParam(message, objects), null);
}
}
@Override
public void debug(Throwable throwable, String message, Object... objects) {
if (this.isDebugEnable()) {
this.logger(LogLevel.DEBUG, replaceParam(message, objects), throwable);
}
}
@Override
public boolean isDebugEnable() {
return LogLevel.DEBUG.compareTo(Config.Logging.LEVEL) >= 0;
}
@Override
public boolean isInfoEnable() {
return LogLevel.INFO.compareTo(Config.Logging.LEVEL) >= 0;
}
@Override
public boolean isWarnEnable() {
return LogLevel.WARN.compareTo(Config.Logging.LEVEL) >= 0;
}
@Override
public boolean isErrorEnable() {
return LogLevel.ERROR.compareTo(Config.Logging.LEVEL) >= 0;
}
@Override
public boolean isTraceEnabled() {
return LogLevel.TRACE.compareTo(Config.Logging.LEVEL) >= 0;
}
@Override
public void trace(final String message) {
if (this.isTraceEnabled()) {
this.logger(LogLevel.TRACE, message, null);
}
}
@Override
public void trace(final String message, final Object... objects) {
if (this.isTraceEnabled()) {
this.logger(LogLevel.TRACE, replaceParam(message, objects), null);
}
}
@Override
public void trace(final Throwable throwable, final String message, final Object... objects) {
if (this.isTraceEnabled()) {
this.logger(LogLevel.TRACE, replaceParam(message, objects), throwable);
}
}
protected String replaceParam(String message, Object... parameters) {
if (message == null) {
return message;
}
int startSize = 0;
int parametersIndex = 0;
int index;
String tmpMessage = message;
while ((index = message.indexOf("{}", startSize)) != -1) {
if (parametersIndex >= parameters.length) {
break;
}
/**
* @Fix the Illegal group reference issue
*/
tmpMessage = tmpMessage.replaceFirst("\\{\\}", Matcher.quoteReplacement(String.valueOf(parameters[parametersIndex++])));
startSize = index + 2;
}
return tmpMessage;
}
protected void logger(LogLevel level, String message, Throwable e) {
WriterFactory.getLogWriter().write(this.format(level, message, e));
}
/**
* The abstract method left for real loggers.
* Any implementation MUST return string, which will be directly transferred to log destination,
* i.e. log files OR stdout
*
* @param level log level
* @param message log message, which has been interpolated with user-defined parameters.
* @param e throwable if exists
* @return string representation of the log, for example, raw json string for {@link JsonLogger}
*/
protected abstract String format(LogLevel level, String message, Throwable e);
}

@ -0,0 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core;
/**
* The Converter, it is used to convert the LogEvent to the String.
* For JsonLogger, the `getKey()` method is used to generate the key for json.
*/
public interface Converter {
String convert(LogEvent logEvent);
String getKey();
}

@ -0,0 +1,234 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core;
import cn.hippo4j.agent.core.boot.DefaultNamedThreadFactory;
import cn.hippo4j.agent.core.conf.Config;
import cn.hippo4j.agent.core.conf.Constants;
import cn.hippo4j.agent.core.util.RunnableWithExceptionProtection;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
* The <code>FileWriter</code> support async file output, by using a queue as buffer.
*/
public class FileWriter implements IWriter {
private static FileWriter INSTANCE;
private static final Object CREATE_LOCK = new Object();
private FileOutputStream fileOutputStream;
private ArrayBlockingQueue logBuffer;
private volatile int fileSize;
private Pattern filenamePattern = Pattern.compile(Config.Logging.FILE_NAME + "\\.\\d{4}_\\d{2}_\\d{2}_\\d{2}_\\d{2}_\\d{2}");
public static FileWriter get() {
if (INSTANCE == null) {
synchronized (CREATE_LOCK) {
if (INSTANCE == null) {
INSTANCE = new FileWriter();
}
}
}
return INSTANCE;
}
private FileWriter() {
logBuffer = new ArrayBlockingQueue(1024);
final ArrayList<String> outputLogs = new ArrayList<String>(200);
Executors.newSingleThreadScheduledExecutor(new DefaultNamedThreadFactory("LogFileWriter"))
.scheduleAtFixedRate(new RunnableWithExceptionProtection(new Runnable() {
@Override
public void run() {
try {
logBuffer.drainTo(outputLogs);
for (String log : outputLogs) {
writeToFile(log + Constants.LINE_SEPARATOR);
}
try {
if (fileOutputStream != null) {
fileOutputStream.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
} finally {
outputLogs.clear();
}
}
}, new RunnableWithExceptionProtection.CallbackWhenException() {
@Override
public void handle(Throwable t) {
}
}), 0, 1, TimeUnit.SECONDS);
}
/**
* @param message to be written into the file.
*/
private void writeToFile(String message) {
if (prepareWriteStream()) {
try {
fileOutputStream.write(message.getBytes());
fileSize += message.length();
} catch (IOException e) {
e.printStackTrace();
} finally {
switchFile();
}
}
}
private void switchFile() {
if (fileSize > Config.Logging.MAX_FILE_SIZE) {
forceExecute(new Callable() {
@Override
public Object call() throws Exception {
fileOutputStream.flush();
return null;
}
});
forceExecute(new Callable() {
@Override
public Object call() throws Exception {
fileOutputStream.close();
return null;
}
});
forceExecute(new Callable() {
@Override
public Object call() throws Exception {
new File(Config.Logging.DIR, Config.Logging.FILE_NAME).renameTo(new File(Config.Logging.DIR, Config.Logging.FILE_NAME + new SimpleDateFormat(".yyyy_MM_dd_HH_mm_ss")
.format(new Date())));
return null;
}
});
forceExecute(new Callable() {
@Override
public Object call() throws Exception {
fileOutputStream = null;
return null;
}
});
if (Config.Logging.MAX_HISTORY_FILES > 0) {
deleteExpiredFiles();
}
}
}
/**
* load history log file name array
*
* @return history log file name array
*/
private String[] getHistoryFilePath() {
File path = new File(Config.Logging.DIR);
String[] pathArr = path.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return filenamePattern.matcher(name).matches();
}
});
return pathArr;
}
/**
* delete expired log files
*/
private void deleteExpiredFiles() {
String[] historyFileArr = getHistoryFilePath();
if (historyFileArr != null && historyFileArr.length > Config.Logging.MAX_HISTORY_FILES) {
Arrays.sort(historyFileArr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
for (int i = Config.Logging.MAX_HISTORY_FILES; i < historyFileArr.length; i++) {
File expiredFile = new File(Config.Logging.DIR, historyFileArr[i]);
expiredFile.delete();
}
}
}
private void forceExecute(Callable callable) {
try {
callable.call();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @return true if stream is prepared ready.
*/
private boolean prepareWriteStream() {
if (fileOutputStream != null) {
return true;
}
File logFilePath = new File(Config.Logging.DIR);
if (!logFilePath.exists()) {
logFilePath.mkdirs();
} else if (!logFilePath.isDirectory()) {
System.err.println("Log dir(" + Config.Logging.DIR + ") is not a directory.");
}
try {
fileOutputStream = new FileOutputStream(new File(logFilePath, Config.Logging.FILE_NAME), true);
fileSize = Long.valueOf(new File(logFilePath, Config.Logging.FILE_NAME).length()).intValue();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return fileOutputStream != null;
}
/**
* Write log to the queue. W/ performance trade off.
*
* @param message to log
*/
@Override
public void write(String message) {
logBuffer.offer(message);
}
}

@ -15,7 +15,9 @@
* limitations under the License.
*/
package cn.hippo4j.common.executor;
package cn.hippo4j.agent.core.logging.core;
public final class ExecutorFactoryTest {
public interface IWriter {
void write(String message);
}

@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogResolver;
import com.google.gson.Gson;
public class JsonLogResolver implements LogResolver {
private static final Gson GSON = new Gson();
@Override
public ILog getLogger(Class<?> aClass) {
return new JsonLogger(aClass, GSON);
}
@Override
public ILog getLogger(String s) {
return new JsonLogger(s, GSON);
}
}

@ -0,0 +1,80 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core;
import cn.hippo4j.agent.core.logging.core.converters.LiteralConverter;
import com.google.gson.Gson;
import java.util.HashMap;
import java.util.Map;
/**
* An alternative logger for the Hippo4j agent. The default layout is
* {
* "@timestamp": "", // timestamp
* "logger": "", // name of the Logger
* "level": "", // info|debug|warn|error
* "thread": "", // thread where the log method is called
* "message": "", // your log message
* "throwable": "",
* "agent_name" "service_name"
* }
*/
public class JsonLogger extends AbstractLogger {
private final Gson gson;
public JsonLogger(Class<?> targetClass, Gson gson) {
this(targetClass.getSimpleName(), gson);
}
/**
* In the Constructor, the instances of converters are created,
* except those {@link LiteralConverter} since this class is used
* only the literals in {@link PatternLogger} ,
* and thus should not be added to the json log.
*
* @param targetClass the logger class
* @param gson instance of Gson works as json serializer
*/
public JsonLogger(String targetClass, Gson gson) {
super(targetClass);
this.gson = gson;
for (Map.Entry<String, Class<? extends Converter>> entry : DEFAULT_CONVERTER_MAP.entrySet()) {
final Class<? extends Converter> converterClass = entry.getValue();
try {
if (converters instanceof LiteralConverter) {
continue;
}
converters.add(converterClass.newInstance());
} catch (IllegalAccessException | InstantiationException e) {
throw new IllegalStateException("Create Converter error. Class: " + converterClass, e);
}
}
}
@Override
protected String format(LogLevel level, String message, Throwable e) {
LogEvent logEvent = new LogEvent(level, message, e, this.targetClass);
Map<String, String> log = new HashMap<>(this.converters.size());
for (Converter converter : this.converters) {
log.put(converter.getKey(), converter.convert(logEvent));
}
return this.gson.toJson(log);
}
}

@ -0,0 +1,68 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core;
/**
* The representation of logging events. This instance is pass around to the List of Converter.
*/
public class LogEvent {
public LogEvent(LogLevel level, String message, Throwable throwable, String targetClass) {
this.level = level;
this.message = message;
this.throwable = throwable;
this.targetClass = targetClass;
}
private LogLevel level;
private String message;
private Throwable throwable;
private String targetClass;
public String getTargetClass() {
return targetClass;
}
public void setTargetClass(String targetClass) {
this.targetClass = targetClass;
}
public LogLevel getLevel() {
return level;
}
public void setLevel(LogLevel level) {
this.level = level;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Throwable getThrowable() {
return throwable;
}
public void setThrowable(Throwable throwable) {
this.throwable = throwable;
}
}

@ -15,7 +15,8 @@
* limitations under the License.
*/
package cn.hippo4j.auth.toolkit;
package cn.hippo4j.agent.core.logging.core;
public final class JwtTokenUtilTest {
public enum LogLevel {
TRACE, DEBUG, INFO, WARN, ERROR, OFF
}

@ -15,23 +15,21 @@
* limitations under the License.
*/
package cn.hippo4j.core.executor.support;
import org.slf4j.MDC;
import static cn.hippo4j.common.constant.Constants.EXECUTE_TIMEOUT_TRACE;
package cn.hippo4j.agent.core.logging.core;
/**
* Executor context.
* The <code>LogMessageHolder</code> is a {@link String} holder, in order to in-process propagation String across the
* disruptor queue.
*/
public class ExecutorContext {
public class LogMessageHolder {
private String message;
public String getMessage() {
return message;
}
/**
* Put execute timeout trace.
*
* @param executeTimeoutTrace
*/
public static void putExecuteTimeoutTrace(String executeTimeoutTrace) {
MDC.put(EXECUTE_TIMEOUT_TRACE, executeTimeoutTrace);
public void setMessage(String message) {
this.message = message;
}
}

@ -15,7 +15,8 @@
* limitations under the License.
*/
package cn.hippo4j.common.executor;
package cn.hippo4j.agent.core.logging.core;
public final class ThreadPoolManagerTest {
public enum LogOutput {
FILE, CONSOLE
}

@ -0,0 +1,190 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core;
import cn.hippo4j.agent.core.logging.core.converters.LiteralConverter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Parser of LogPattern. It is used to parse a pattern to the List of Converter.
*/
public class Parser {
private final Map<String, Class<? extends Converter>> convertMaps;
enum State {
LITERAL_STATE, KEYWORD_STATE
}
public static final char ESCAPE_CHAR = '\\';
public static final char PERCENT_CHAR = '%';
private final String pattern;
private final int patternLength;
private int pointer = 0;
private State state = State.LITERAL_STATE;
public Parser(String pattern, Map<String, Class<? extends Converter>> convertMaps) {
if (pattern == null || pattern.length() == 0) {
throw new IllegalArgumentException("null or empty pattern string not allowed");
}
this.convertMaps = convertMaps;
this.pattern = pattern;
this.patternLength = pattern.length();
}
public List<Converter> parse() {
List<Converter> patternConverters = new ArrayList<Converter>();
StringBuilder buf = new StringBuilder();
while (pointer < patternLength) {
char c = pattern.charAt(pointer);
pointer++;
switch (state) {
case LITERAL_STATE:
handleLiteralState(c, buf, patternConverters);
break;
case KEYWORD_STATE:
handleKeywordState(c, buf, patternConverters);
break;
default:
}
}
switch (state) {
case LITERAL_STATE:
addConverter(buf, patternConverters, LiteralConverter.class);
break;
case KEYWORD_STATE:
addConverterWithKeyword(buf, patternConverters);
break;
default:
}
return combineLiteral(patternConverters);
}
private List<Converter> combineLiteral(List<Converter> patternConverters) {
List<Converter> converterList = new ArrayList<Converter>();
StringBuilder stringBuilder = new StringBuilder();
for (Converter patternConverter : patternConverters) {
if (patternConverter instanceof LiteralConverter) {
stringBuilder.append(patternConverter.convert(null));
} else {
if (stringBuilder.length() > 0) {
converterList.add(new LiteralConverter(stringBuilder.toString()));
stringBuilder.setLength(0);
}
converterList.add(patternConverter);
}
}
return converterList;
}
private void handleKeywordState(char c, StringBuilder buf, List<Converter> patternConverters) {
if (Character.isJavaIdentifierPart(c)) {
buf.append(c);
} else if (c == PERCENT_CHAR) {
addConverterWithKeyword(buf, patternConverters);
} else {
addConverterWithKeyword(buf, patternConverters);
if (c == ESCAPE_CHAR) {
escape("%", buf);
} else {
buf.append(c);
}
state = State.LITERAL_STATE;
}
}
private void addConverterWithKeyword(StringBuilder buf, List<Converter> patternConverters) {
String keyword = buf.toString();
if (convertMaps.containsKey(keyword)) {
addConverter(buf, patternConverters, convertMaps.get(keyword));
} else {
buf.insert(0, "%");
addConverter(buf, patternConverters, LiteralConverter.class);
}
}
private void handleLiteralState(char c, StringBuilder buf, List<Converter> patternConverters) {
switch (c) {
case ESCAPE_CHAR:
escape("%", buf);
break;
case PERCENT_CHAR:
addConverter(buf, patternConverters, LiteralConverter.class);
state = State.KEYWORD_STATE;
break;
default:
buf.append(c);
}
}
private void escape(String escapeChars, StringBuilder buf) {
if (pointer < patternLength) {
char next = pattern.charAt(pointer++);
escape(escapeChars, buf, next);
}
}
private void addConverter(StringBuilder buf, List<Converter> patternConverters, Class<? extends Converter> aClass) {
if (buf.length() > 0) {
String result = buf.toString();
if (LiteralConverter.class.equals(aClass)) {
patternConverters.add(new LiteralConverter(result));
} else {
try {
patternConverters.add(aClass.newInstance());
} catch (Exception e) {
throw new IllegalStateException("Create Converter error. Class: " + aClass, e);
}
}
buf.setLength(0);
}
}
private void escape(String escapeChars, StringBuilder buf, char next) {
if (escapeChars.indexOf(next) >= 0) {
buf.append(next);
} else {
switch (next) {
case '_':
// the \_ sequence is swallowed
break;
case '\\':
buf.append(next);
break;
case 't':
buf.append('\t');
break;
case 'r':
buf.append('\r');
break;
case 'n':
buf.append('\n');
break;
default:
throw new IllegalArgumentException("Illegal char " + next + ". It not allowed as escape characters.");
}
}
}
}

@ -0,0 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core;
import cn.hippo4j.agent.core.conf.Config;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogResolver;
public class PatternLogResolver implements LogResolver {
@Override
public ILog getLogger(Class<?> clazz) {
return new PatternLogger(clazz, Config.Logging.PATTERN);
}
@Override
public ILog getLogger(String clazz) {
return new PatternLogger(clazz, Config.Logging.PATTERN);
}
}

@ -0,0 +1,64 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.util.StringUtil;
/**
* A flexible Logger configurable with pattern string. This is default implementation of {@link ILog} This can parse a
* pattern to the List of converter with Parser. We package LogEvent with message, level,timestamp ..., passing around
* to the List of converter to concat actually Log-String.
*/
public class PatternLogger extends AbstractLogger {
public static final String DEFAULT_PATTERN = "%level %timestamp %thread %class : %msg %throwable";
private String pattern;
public PatternLogger(Class<?> targetClass, String pattern) {
this(targetClass.getSimpleName(), pattern);
}
public PatternLogger(String targetClass, String pattern) {
super(targetClass);
this.setPattern(pattern);
}
public String getPattern() {
return pattern;
}
public void setPattern(String pattern) {
if (StringUtil.isEmpty(pattern)) {
pattern = DEFAULT_PATTERN;
}
this.pattern = pattern;
this.converters = new Parser(pattern, DEFAULT_CONVERTER_MAP).parse();
}
@Override
protected String format(LogLevel level, String message, Throwable t) {
LogEvent logEvent = new LogEvent(level, message, t, targetClass);
StringBuilder stringBuilder = new StringBuilder();
for (Converter converter : this.converters) {
stringBuilder.append(converter.convert(logEvent));
}
return stringBuilder.toString();
}
}

@ -15,7 +15,8 @@
* limitations under the License.
*/
package cn.hippo4j.auth.secuity;
package cn.hippo4j.agent.core.logging.core;
public final class JwtTokenManagerTest {
public enum ResolverType {
JSON, PATTERN
}

@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core;
import java.io.PrintStream;
public enum SystemOutWriter implements IWriter {
INSTANCE;
/**
* Tricky codes for avoiding style-check. Because, in here, "system.out.println" is the only choice to output logs.
*/
@Override
public void write(String message) {
PrintStream out = System.out;
out.println(message);
}
}

@ -0,0 +1,59 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core;
import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException;
import cn.hippo4j.agent.core.boot.AgentPackagePath;
import cn.hippo4j.agent.core.conf.Config;
import cn.hippo4j.agent.core.conf.SnifferConfigInitializer;
import cn.hippo4j.agent.core.plugin.PluginFinder;
import cn.hippo4j.agent.core.util.StringUtil;
public class WriterFactory {
private static IWriter WRITER;
public static IWriter getLogWriter() {
switch (Config.Logging.OUTPUT) {
case FILE:
if (WRITER != null) {
return WRITER;
}
if (SnifferConfigInitializer.isInitCompleted()
&& PluginFinder.isPluginInitCompleted()
&& AgentPackagePath.isPathFound()) {
if (StringUtil.isEmpty(Config.Logging.DIR)) {
try {
Config.Logging.DIR = AgentPackagePath.getPath() + "/logs";
} catch (AgentPackageNotFoundException e) {
e.printStackTrace();
}
}
WRITER = FileWriter.get();
} else {
return SystemOutWriter.INSTANCE;
}
break;
default:
return SystemOutWriter.INSTANCE;
}
return WRITER;
}
}

@ -0,0 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core.converters;
import cn.hippo4j.agent.core.conf.Config;
import cn.hippo4j.agent.core.logging.core.Converter;
import cn.hippo4j.agent.core.logging.core.LogEvent;
public class AgentNameConverter implements Converter {
@Override
public String convert(LogEvent logEvent) {
return Config.Agent.SERVICE_NAME;
}
@Override
public String getKey() {
return "agent_name";
}
}

@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core.converters;
import cn.hippo4j.agent.core.logging.core.Converter;
import cn.hippo4j.agent.core.logging.core.LogEvent;
/**
* Just return logEvent.getTargetClass().
*/
public class ClassConverter implements Converter {
@Override
public String convert(LogEvent logEvent) {
return logEvent.getTargetClass();
}
@Override
public String getKey() {
return "logger";
}
}

@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core.converters;
import cn.hippo4j.agent.core.logging.core.Converter;
import cn.hippo4j.agent.core.logging.core.LogEvent;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* The Converter is used to return a now date with format.
*/
public class DateConverter implements Converter {
@Override
public String convert(LogEvent logEvent) {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
}
@Override
public String getKey() {
return "@timestamp";
}
}

@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core.converters;
import cn.hippo4j.agent.core.logging.core.Converter;
import cn.hippo4j.agent.core.logging.core.LogEvent;
/**
* Just return logEvent.getLevel().name()
*/
public class LevelConverter implements Converter {
@Override
public String convert(LogEvent logEvent) {
return logEvent.getLevel().name();
}
@Override
public String getKey() {
return "level";
}
}

@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core.converters;
import cn.hippo4j.agent.core.logging.core.Converter;
import cn.hippo4j.agent.core.logging.core.LogEvent;
/**
* This Converter is used to return the literal.
*/
public class LiteralConverter implements Converter {
private final String literal;
public LiteralConverter(String literal) {
this.literal = literal;
}
@Override
public String convert(LogEvent logEvent) {
return literal;
}
@Override
public String getKey() {
return "";
}
}

@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core.converters;
import cn.hippo4j.agent.core.logging.core.Converter;
import cn.hippo4j.agent.core.logging.core.LogEvent;
/**
* Just return the logEvent.getMessage()
*/
public class MessageConverter implements Converter {
@Override
public String convert(LogEvent logEvent) {
return logEvent.getMessage();
}
@Override
public String getKey() {
return "message";
}
}

@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core.converters;
import cn.hippo4j.agent.core.logging.core.Converter;
import cn.hippo4j.agent.core.logging.core.LogEvent;
/**
* Just return the Thread.currentThread().getName()
*/
public class ThreadConverter implements Converter {
@Override
public String convert(LogEvent logEvent) {
return Thread.currentThread().getName();
}
@Override
public String getKey() {
return "thread";
}
}

@ -0,0 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.logging.core.converters;
import cn.hippo4j.agent.core.conf.Constants;
import cn.hippo4j.agent.core.logging.core.Converter;
import cn.hippo4j.agent.core.logging.core.LogEvent;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* Return the StackTrace of String with logEvent.getThrowable()
*/
public class ThrowableConverter implements Converter {
@Override
public String convert(LogEvent logEvent) {
Throwable t = logEvent.getThrowable();
return t == null ? "" : format(t);
}
public static String format(Throwable t) {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
t.printStackTrace(new java.io.PrintWriter(buf, true));
String expMessage = buf.toString();
try {
buf.close();
} catch (IOException e) {
e.printStackTrace();
}
return Constants.LINE_SEPARATOR + expMessage;
}
@Override
public String getKey() {
return "throwable";
}
}

@ -0,0 +1,104 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.os;
import java.lang.management.ManagementFactory;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
public class OSUtil {
private static volatile String OS_NAME;
private static volatile String HOST_NAME;
private static volatile List<String> IPV4_LIST;
private static volatile int PROCESS_NO = 0;
public static String getOsName() {
if (OS_NAME == null) {
OS_NAME = System.getProperty("os.name");
}
return OS_NAME;
}
public static String getHostName() {
if (HOST_NAME == null) {
try {
InetAddress host = InetAddress.getLocalHost();
HOST_NAME = host.getHostName();
} catch (UnknownHostException e) {
HOST_NAME = "unknown";
}
}
return HOST_NAME;
}
public static List<String> getAllIPV4() {
if (IPV4_LIST == null) {
IPV4_LIST = new LinkedList<>();
try {
Enumeration<NetworkInterface> interfs = NetworkInterface.getNetworkInterfaces();
while (interfs.hasMoreElements()) {
NetworkInterface networkInterface = interfs.nextElement();
Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
while (inetAddresses.hasMoreElements()) {
InetAddress address = inetAddresses.nextElement();
if (address instanceof Inet4Address) {
String addressStr = address.getHostAddress();
if ("127.0.0.1".equals(addressStr)) {
continue;
} else if ("localhost".equals(addressStr)) {
continue;
}
IPV4_LIST.add(addressStr);
}
}
}
} catch (SocketException e) {
}
}
return IPV4_LIST;
}
public static String getIPV4() {
final List<String> allIPV4 = getAllIPV4();
if (allIPV4.size() > 0) {
return allIPV4.get(0);
} else {
return "no-hostname";
}
}
public static int getProcessNo() {
if (PROCESS_NO == 0) {
try {
PROCESS_NO = Integer.parseInt(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
} catch (Exception e) {
PROCESS_NO = -1;
}
}
return PROCESS_NO;
}
}

@ -0,0 +1,27 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.os;
import java.lang.management.ManagementFactory;
public class ProcessorUtil {
public static int getNumberOfProcessors() {
return ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
}
}

@ -0,0 +1,202 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogManager;
import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
import cn.hippo4j.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.ClassEnhancePluginDefine;
import cn.hippo4j.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point;
import cn.hippo4j.agent.core.plugin.interceptor.v2.StaticMethodsInterceptV2Point;
import cn.hippo4j.agent.core.plugin.match.ClassMatch;
import cn.hippo4j.agent.core.util.CollectionUtil;
import cn.hippo4j.agent.core.util.StringUtil;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import java.util.List;
/**
* Basic abstract class of all sky-walking auto-instrumentation plugins.
* <p>
* It provides the outline of enhancing the target class. If you want to know more about enhancing, you should go to see
* {@link ClassEnhancePluginDefine}
*/
public abstract class AbstractClassEnhancePluginDefine {
private static final ILog LOGGER = LogManager.getLogger(AbstractClassEnhancePluginDefine.class);
/**
* New field name.
*/
public static final String CONTEXT_ATTR_NAME = "_$EnhancedClassField_ws";
/**
* Main entrance of enhancing the class.
*
* @param typeDescription target class description.
* @param builder byte-buddy's builder to manipulate target class's bytecode.
* @param classLoader load the given transformClass
* @return the new builder, or <code>null</code> if not be enhanced.
* @throws PluginException when set builder failure.
*/
public DynamicType.Builder<?> define(TypeDescription typeDescription, DynamicType.Builder<?> builder,
ClassLoader classLoader, EnhanceContext context) throws PluginException {
String interceptorDefineClassName = this.getClass().getName();
String transformClassName = typeDescription.getTypeName();
if (StringUtil.isEmpty(transformClassName)) {
LOGGER.warn("classname of being intercepted is not defined by {}.", interceptorDefineClassName);
return null;
}
LOGGER.debug("prepare to enhance class {} by {}.", transformClassName, interceptorDefineClassName);
WitnessFinder finder = WitnessFinder.INSTANCE;
/**
* find witness classes for enhance class
*/
String[] witnessClasses = witnessClasses();
if (witnessClasses != null) {
for (String witnessClass : witnessClasses) {
if (!finder.exist(witnessClass, classLoader)) {
LOGGER.warn("enhance class {} by plugin {} is not activated. Witness class {} does not exist.", transformClassName, interceptorDefineClassName, witnessClass);
return null;
}
}
}
List<WitnessMethod> witnessMethods = witnessMethods();
if (!CollectionUtil.isEmpty(witnessMethods)) {
for (WitnessMethod witnessMethod : witnessMethods) {
if (!finder.exist(witnessMethod, classLoader)) {
LOGGER.warn("enhance class {} by plugin {} is not activated. Witness method {} does not exist.", transformClassName, interceptorDefineClassName, witnessMethod);
return null;
}
}
}
/**
* find origin class source code for interceptor
*/
DynamicType.Builder<?> newClassBuilder = this.enhance(typeDescription, builder, classLoader, context);
context.initializationStageCompleted();
LOGGER.debug("enhance class {} by {} completely.", transformClassName, interceptorDefineClassName);
return newClassBuilder;
}
/**
* Begin to define how to enhance class. After invoke this method, only means definition is finished.
*
* @param typeDescription target class description
* @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
* @return new byte-buddy's builder for further manipulation.
*/
protected DynamicType.Builder<?> enhance(TypeDescription typeDescription, DynamicType.Builder<?> newClassBuilder,
ClassLoader classLoader, EnhanceContext context) throws PluginException {
newClassBuilder = this.enhanceClass(typeDescription, newClassBuilder, classLoader);
newClassBuilder = this.enhanceInstance(typeDescription, newClassBuilder, classLoader, context);
return newClassBuilder;
}
/**
* Enhance a class to intercept constructors and class instance methods.
*
* @param typeDescription target class description
* @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
* @return new byte-buddy's builder for further manipulation.
*/
protected abstract DynamicType.Builder<?> enhanceInstance(TypeDescription typeDescription,
DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader,
EnhanceContext context) throws PluginException;
/**
* Enhance a class to intercept class static methods.
*
* @param typeDescription target class description
* @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
* @return new byte-buddy's builder for further manipulation.
*/
protected abstract DynamicType.Builder<?> enhanceClass(TypeDescription typeDescription, DynamicType.Builder<?> newClassBuilder,
ClassLoader classLoader) throws PluginException;
/**
* Define the {@link ClassMatch} for filtering class.
*
* @return {@link ClassMatch}
*/
protected abstract ClassMatch enhanceClass();
/**
* Witness classname list. Why need witness classname? Let's see like this: A library existed two released versions
* (like 1.0, 2.0), which include the same target classes, but because of version iterator, they may have the same
* name, but different methods, or different method arguments list. So, if I want to target the particular version
* (let's say 1.0 for example), version number is obvious not an option, this is the moment you need "Witness
* classes". You can add any classes only in this particular release version ( something like class
* com.company.1.x.A, only in 1.0 ), and you can achieve the goal.
*/
protected String[] witnessClasses() {
return new String[]{};
}
protected List<WitnessMethod> witnessMethods() {
return null;
}
public boolean isBootstrapInstrumentation() {
return false;
}
/**
* Constructor methods intercept point. See {@link ConstructorInterceptPoint}
*
* @return collections of {@link ConstructorInterceptPoint}
*/
public abstract ConstructorInterceptPoint[] getConstructorsInterceptPoints();
/**
* Instance methods intercept point. See {@link InstanceMethodsInterceptPoint}
*
* @return collections of {@link InstanceMethodsInterceptPoint}
*/
public abstract InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints();
/**
* Instance methods intercept v2 point. See {@link InstanceMethodsInterceptV2Point}
*
* @return collections of {@link InstanceMethodsInterceptV2Point}
*/
public abstract InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points();
/**
* Static methods intercept point. See {@link StaticMethodsInterceptPoint}
*
* @return collections of {@link StaticMethodsInterceptPoint}
*/
public abstract StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints();
/**
* Instance methods intercept v2 point. See {@link InstanceMethodsInterceptV2Point}
*
* @return collections of {@link InstanceMethodsInterceptV2Point}
*/
public abstract StaticMethodsInterceptV2Point[] getStaticMethodsInterceptV2Points();
}

@ -0,0 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
/**
* All ByteBuddy core classes required to expose, including open edge for JDK 9+ module, or Bootstrap instrumentation.
*/
public class ByteBuddyCoreClasses {
private static final String SHADE_PACKAGE = "cn.hippo4j.agent.dependencies.";
public static final String[] CLASSES = {
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.RuntimeType",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.This",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.AllArguments",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.AllArguments$Assignment",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.SuperCall",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.Origin",
SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.Morph",
};
}

@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
import cn.hippo4j.agent.core.plugin.loader.AgentClassLoader;
import cn.hippo4j.agent.core.plugin.loader.InstrumentationLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
/**
* The plugin can be inserted into the kernel by implementing this spi return PluginDefine list.
*/
public enum DynamicPluginLoader {
INSTANCE;
public List<AbstractClassEnhancePluginDefine> load(AgentClassLoader classLoader) {
List<AbstractClassEnhancePluginDefine> all = new ArrayList<AbstractClassEnhancePluginDefine>();
for (InstrumentationLoader instrumentationLoader : ServiceLoader.load(InstrumentationLoader.class, classLoader)) {
List<AbstractClassEnhancePluginDefine> plugins = instrumentationLoader.load(classLoader);
if (plugins != null && !plugins.isEmpty()) {
all.addAll(plugins);
}
}
return all;
}
}

@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.ClassEnhancePluginDefine;
/**
* The <code>EnhanceContext</code> represents the context or status for processing a class.
* <p>
* Based on this context, the plugin core {@link ClassEnhancePluginDefine} knows how to process the specific steps for
* every particular plugin.
*/
public class EnhanceContext {
private boolean isEnhanced = false;
/**
* The object has already been enhanced or extended. e.g. added the new field, or implemented the new interface
*/
private boolean objectExtended = false;
public boolean isEnhanced() {
return isEnhanced;
}
public void initializationStageCompleted() {
isEnhanced = true;
}
public boolean isObjectExtended() {
return objectExtended;
}
public void extendObjectCompleted() {
objectExtended = true;
}
}

@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException;
import cn.hippo4j.agent.core.boot.AgentPackagePath;
import cn.hippo4j.agent.core.conf.Config;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogManager;
import net.bytebuddy.dynamic.DynamicType;
import java.io.File;
import java.io.IOException;
/**
* The manipulated class output. Write the dynamic classes to the `debugging` folder, when we need to do some debug and
* recheck.
*/
public enum InstrumentDebuggingClass {
INSTANCE;
private static final ILog LOGGER = LogManager.getLogger(InstrumentDebuggingClass.class);
private File debuggingClassesRootPath;
public void log(DynamicType dynamicType) {
if (!Config.Agent.IS_OPEN_DEBUGGING_CLASS) {
return;
}
/**
* try to do I/O things in synchronized way, to avoid unexpected situations.
*/
synchronized (INSTANCE) {
try {
if (debuggingClassesRootPath == null) {
try {
debuggingClassesRootPath = new File(AgentPackagePath.getPath(), "/debugging");
if (!debuggingClassesRootPath.exists()) {
debuggingClassesRootPath.mkdir();
}
} catch (AgentPackageNotFoundException e) {
LOGGER.error(e, "Can't find the root path for creating /debugging folder.");
}
}
try {
dynamicType.saveIn(debuggingClassesRootPath);
} catch (IOException e) {
LOGGER.error(e, "Can't save class {} to file." + dynamicType.getTypeDescription().getActualName());
}
} catch (Throwable t) {
LOGGER.error(t, "Save debugging classes fail.");
}
}
}
}

@ -0,0 +1,81 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogManager;
import cn.hippo4j.agent.core.plugin.loader.AgentClassLoader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* Plugins finder. Use {@link PluginResourcesResolver} to find all plugins, and ask {@link PluginCfg} to load all plugin
* definitions.
*/
public class PluginBootstrap {
private static final ILog LOGGER = LogManager.getLogger(PluginBootstrap.class);
/**
* load all plugins.
*
* @return plugin definition list.
*/
public List<AbstractClassEnhancePluginDefine> loadPlugins() throws AgentPackageNotFoundException {
AgentClassLoader.initDefaultLoader();
PluginResourcesResolver resolver = new PluginResourcesResolver();
List<URL> resources = resolver.getResources();
if (resources == null || resources.size() == 0) {
LOGGER.info("no plugin files (hippo4j-plugin.def) found, continue to start application.");
return new ArrayList<AbstractClassEnhancePluginDefine>();
}
for (URL pluginUrl : resources) {
try {
PluginCfg.INSTANCE.load(pluginUrl.openStream());
} catch (Throwable t) {
LOGGER.error(t, "plugin file [{}] init failure.", pluginUrl);
}
}
List<PluginDefine> pluginClassList = PluginCfg.INSTANCE.getPluginClassList();
List<AbstractClassEnhancePluginDefine> plugins = new ArrayList<AbstractClassEnhancePluginDefine>();
for (PluginDefine pluginDefine : pluginClassList) {
try {
LOGGER.debug("loading plugin class {}.", pluginDefine.getDefineClass());
AbstractClassEnhancePluginDefine plugin = (AbstractClassEnhancePluginDefine) Class.forName(pluginDefine.getDefineClass(), true, AgentClassLoader
.getDefault()).newInstance();
plugins.add(plugin);
} catch (Throwable t) {
LOGGER.error(t, "load plugin [{}] failure.", pluginDefine.getDefineClass());
}
}
plugins.addAll(DynamicPluginLoader.INSTANCE.load(AgentClassLoader.getDefault()));
return plugins;
}
}

@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogManager;
import cn.hippo4j.agent.core.plugin.exception.IllegalPluginDefineException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
public enum PluginCfg {
INSTANCE;
private static final ILog LOGGER = LogManager.getLogger(PluginCfg.class);
private List<PluginDefine> pluginClassList = new ArrayList<PluginDefine>();
private PluginSelector pluginSelector = new PluginSelector();
void load(InputStream input) throws IOException {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String pluginDefine;
while ((pluginDefine = reader.readLine()) != null) {
try {
if (pluginDefine.trim().length() == 0 || pluginDefine.startsWith("#")) {
continue;
}
PluginDefine plugin = PluginDefine.build(pluginDefine);
pluginClassList.add(plugin);
} catch (IllegalPluginDefineException e) {
LOGGER.error(e, "Failed to format plugin({}) define.", pluginDefine);
}
}
pluginClassList = pluginSelector.select(pluginClassList);
} finally {
input.close();
}
}
public List<PluginDefine> getPluginClassList() {
return pluginClassList;
}
}

@ -0,0 +1,62 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
import cn.hippo4j.agent.core.plugin.exception.IllegalPluginDefineException;
import cn.hippo4j.agent.core.util.StringUtil;
public class PluginDefine {
/**
* Plugin name.
*/
private String name;
/**
* The class name of plugin defined.
*/
private String defineClass;
private PluginDefine(String name, String defineClass) {
this.name = name;
this.defineClass = defineClass;
}
public static PluginDefine build(String define) throws IllegalPluginDefineException {
if (StringUtil.isEmpty(define)) {
throw new IllegalPluginDefineException(define);
}
String[] pluginDefine = define.split("=");
if (pluginDefine.length != 2) {
throw new IllegalPluginDefineException(define);
}
String pluginName = pluginDefine[0];
String defineClass = pluginDefine[1];
return new PluginDefine(pluginName, defineClass);
}
public String getDefineClass() {
return defineClass;
}
public String getName() {
return name;
}
}

@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
public class PluginException extends RuntimeException {
private static final long serialVersionUID = -6020188711867490724L;
public PluginException(String message) {
super(message);
}
public PluginException(String message, Throwable cause) {
super(message, cause);
}
}

@ -0,0 +1,121 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
import cn.hippo4j.agent.core.plugin.bytebuddy.AbstractJunction;
import cn.hippo4j.agent.core.plugin.match.ClassMatch;
import cn.hippo4j.agent.core.plugin.match.IndirectMatch;
import cn.hippo4j.agent.core.plugin.match.NameMatch;
import cn.hippo4j.agent.core.plugin.match.ProtectiveShieldMatcher;
import net.bytebuddy.description.NamedElement;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.not;
/**
* The <code>PluginFinder</code> represents a finder , which assist to find the one from the given {@link
* AbstractClassEnhancePluginDefine} list.
*/
public class PluginFinder {
private final Map<String, LinkedList<AbstractClassEnhancePluginDefine>> nameMatchDefine = new HashMap<String, LinkedList<AbstractClassEnhancePluginDefine>>();
private final List<AbstractClassEnhancePluginDefine> signatureMatchDefine = new ArrayList<AbstractClassEnhancePluginDefine>();
private final List<AbstractClassEnhancePluginDefine> bootstrapClassMatchDefine = new ArrayList<AbstractClassEnhancePluginDefine>();
private static boolean IS_PLUGIN_INIT_COMPLETED = false;
public PluginFinder(List<AbstractClassEnhancePluginDefine> plugins) {
for (AbstractClassEnhancePluginDefine plugin : plugins) {
ClassMatch match = plugin.enhanceClass();
if (match == null) {
continue;
}
if (match instanceof NameMatch) {
NameMatch nameMatch = (NameMatch) match;
LinkedList<AbstractClassEnhancePluginDefine> pluginDefines = nameMatchDefine.get(nameMatch.getClassName());
if (pluginDefines == null) {
pluginDefines = new LinkedList<AbstractClassEnhancePluginDefine>();
nameMatchDefine.put(nameMatch.getClassName(), pluginDefines);
}
pluginDefines.add(plugin);
} else {
signatureMatchDefine.add(plugin);
}
if (plugin.isBootstrapInstrumentation()) {
bootstrapClassMatchDefine.add(plugin);
}
}
}
public List<AbstractClassEnhancePluginDefine> find(TypeDescription typeDescription) {
List<AbstractClassEnhancePluginDefine> matchedPlugins = new LinkedList<AbstractClassEnhancePluginDefine>();
String typeName = typeDescription.getTypeName();
if (nameMatchDefine.containsKey(typeName)) {
matchedPlugins.addAll(nameMatchDefine.get(typeName));
}
for (AbstractClassEnhancePluginDefine pluginDefine : signatureMatchDefine) {
IndirectMatch match = (IndirectMatch) pluginDefine.enhanceClass();
if (match.isMatch(typeDescription)) {
matchedPlugins.add(pluginDefine);
}
}
return matchedPlugins;
}
public ElementMatcher<? super TypeDescription> buildMatch() {
ElementMatcher.Junction judge = new AbstractJunction<NamedElement>() {
@Override
public boolean matches(NamedElement target) {
return nameMatchDefine.containsKey(target.getActualName());
}
};
judge = judge.and(not(isInterface()));
for (AbstractClassEnhancePluginDefine define : signatureMatchDefine) {
ClassMatch match = define.enhanceClass();
if (match instanceof IndirectMatch) {
judge = judge.or(((IndirectMatch) match).buildJunction());
}
}
return new ProtectiveShieldMatcher(judge);
}
public List<AbstractClassEnhancePluginDefine> getBootstrapClassMatchDefine() {
return bootstrapClassMatchDefine;
}
public static void pluginInitCompleted() {
IS_PLUGIN_INIT_COMPLETED = true;
}
public static boolean isPluginInitCompleted() {
return IS_PLUGIN_INIT_COMPLETED;
}
}

@ -0,0 +1,55 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogManager;
import cn.hippo4j.agent.core.plugin.loader.AgentClassLoader;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
/**
* Use the current classloader to read all plugin define file. The file must be named 'hippo4j-plugin.def'
*/
public class PluginResourcesResolver {
private static final ILog LOGGER = LogManager.getLogger(PluginResourcesResolver.class);
public List<URL> getResources() {
List<URL> cfgUrlPaths = new ArrayList<URL>();
Enumeration<URL> urls;
try {
urls = AgentClassLoader.getDefault().getResources("hippo4j-plugin.def");
while (urls.hasMoreElements()) {
URL pluginUrl = urls.nextElement();
cfgUrlPaths.add(pluginUrl);
LOGGER.info("find hippo4j plugin define in {}", pluginUrl);
}
return cfgUrlPaths;
} catch (IOException e) {
LOGGER.error("read resources failure.", e);
}
return null;
}
}

@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import cn.hippo4j.agent.core.conf.Config;
import static cn.hippo4j.agent.core.conf.Config.Plugin.EXCLUDE_PLUGINS;
/**
* Select some plugins in activated plugins
*/
public class PluginSelector {
/**
* Exclude activated plugins
*
* @param pluginDefines the pluginDefines is loaded from activations directory or plugins directory
* @return real activate plugins
* @see Config.Plugin#EXCLUDE_PLUGINS
*/
public List<PluginDefine> select(List<PluginDefine> pluginDefines) {
if (!EXCLUDE_PLUGINS.isEmpty()) {
List<String> excludes = Arrays.asList(EXCLUDE_PLUGINS.toLowerCase().split(","));
return pluginDefines.stream()
.filter(item -> !excludes.contains(item.getName().toLowerCase()))
.collect(Collectors.toList());
}
return pluginDefines;
}
}

@ -0,0 +1,84 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
import net.bytebuddy.pool.TypePool;
import java.util.HashMap;
import java.util.Map;
/**
* The <code>WitnessFinder</code> represents a pool of {@link TypePool}s, each {@link TypePool} matches a {@link
* ClassLoader}, which helps to find the class declaration existed or not.
*/
public enum WitnessFinder {
INSTANCE;
private final Map<ClassLoader, TypePool> poolMap = new HashMap<ClassLoader, TypePool>();
/**
* @param classLoader for finding the witnessClass
* @return true, if the given witnessClass exists, through the given classLoader.
*/
public boolean exist(String witnessClass, ClassLoader classLoader) {
return getResolution(witnessClass, classLoader)
.isResolved();
}
/**
* get TypePool.Resolution of the witness class
* @param witnessClass class name
* @param classLoader classLoader for finding the witnessClass
* @return TypePool.Resolution
*/
private TypePool.Resolution getResolution(String witnessClass, ClassLoader classLoader) {
ClassLoader mappingKey = classLoader == null ? NullClassLoader.INSTANCE : classLoader;
if (!poolMap.containsKey(mappingKey)) {
synchronized (poolMap) {
if (!poolMap.containsKey(mappingKey)) {
TypePool classTypePool = classLoader == null ? TypePool.Default.ofBootLoader() : TypePool.Default.of(classLoader);
poolMap.put(mappingKey, classTypePool);
}
}
}
TypePool typePool = poolMap.get(mappingKey);
return typePool.describe(witnessClass);
}
/**
* @param classLoader for finding the witness method
* @return true, if the given witness method exists, through the given classLoader.
*/
public boolean exist(WitnessMethod witnessMethod, ClassLoader classLoader) {
TypePool.Resolution resolution = getResolution(witnessMethod.getDeclaringClassName(), classLoader);
if (!resolution.isResolved()) {
return false;
}
return !resolution.resolve()
.getDeclaredMethods()
.filter(witnessMethod.getElementMatcher())
.isEmpty();
}
}
final class NullClassLoader extends ClassLoader {
static NullClassLoader INSTANCE = new NullClassLoader();
}

@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.matcher.ElementMatcher;
/**
* Witness Method for plugin activation
*/
@ToString
@RequiredArgsConstructor
public class WitnessMethod {
/**
* the class or interface name where the witness method is declared.
*/
@Getter
private final String declaringClassName;
/**
* matcher to match the witness method
*/
@Getter
private final ElementMatcher<? super MethodDescription.InDefinedShape> elementMatcher;
}

@ -0,0 +1,301 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin.bootstrap;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogManager;
import cn.hippo4j.agent.core.plugin.AbstractClassEnhancePluginDefine;
import cn.hippo4j.agent.core.plugin.ByteBuddyCoreClasses;
import cn.hippo4j.agent.core.plugin.InstrumentDebuggingClass;
import cn.hippo4j.agent.core.plugin.PluginException;
import cn.hippo4j.agent.core.plugin.PluginFinder;
import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
import cn.hippo4j.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
import cn.hippo4j.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point;
import cn.hippo4j.agent.core.plugin.interceptor.v2.StaticMethodsInterceptV2Point;
import cn.hippo4j.agent.core.plugin.jdk9module.JDK9ModuleExporter;
import cn.hippo4j.agent.core.plugin.loader.AgentClassLoader;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassInjector;
import net.bytebuddy.pool.TypePool;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.Instrumentation;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static net.bytebuddy.matcher.ElementMatchers.named;
/**
* If there is Bootstrap instrumentation plugin declared in plugin list, BootstrapInstrumentBoost inject the necessary
* classes into bootstrap class loader, including generated dynamic delegate classes.
*/
public class BootstrapInstrumentBoost {
private static final ILog LOGGER = LogManager.getLogger(BootstrapInstrumentBoost.class);
private static final String[] HIGH_PRIORITY_CLASSES = {
"cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist",
"cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor",
"cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor",
"cn.hippo4j.agent.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor",
"cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog",
"cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance",
"cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable",
"cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult",
// interceptor v2
"cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2",
"cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2",
"cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext",
};
private static String INSTANCE_METHOD_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.InstanceMethodInterTemplate";
private static String INSTANCE_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.InstanceMethodInterWithOverrideArgsTemplate";
private static String CONSTRUCTOR_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.ConstructorInterTemplate";
private static String STATIC_METHOD_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.StaticMethodInterTemplate";
private static String STATIC_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.StaticMethodInterWithOverrideArgsTemplate";
private static String INSTANCE_METHOD_V2_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.v2.InstanceMethodInterV2Template";
private static String INSTANCE_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.v2.InstanceMethodInterV2WithOverrideArgsTemplate";
private static String STATIC_METHOD_V2_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.v2.StaticMethodInterV2Template";
private static String STATIC_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.v2.StaticMethodInterV2WithOverrideArgsTemplate";
public static AgentBuilder inject(PluginFinder pluginFinder, Instrumentation instrumentation,
AgentBuilder agentBuilder, JDK9ModuleExporter.EdgeClasses edgeClasses) throws PluginException {
Map<String, byte[]> classesTypeMap = new LinkedHashMap<>();
if (!prepareJREInstrumentation(pluginFinder, classesTypeMap)) {
return agentBuilder;
}
if (!prepareJREInstrumentationV2(pluginFinder, classesTypeMap)) {
return agentBuilder;
}
for (String highPriorityClass : HIGH_PRIORITY_CLASSES) {
loadHighPriorityClass(classesTypeMap, highPriorityClass);
}
for (String highPriorityClass : ByteBuddyCoreClasses.CLASSES) {
loadHighPriorityClass(classesTypeMap, highPriorityClass);
}
/**
* Prepare to open edge of necessary classes.
*/
for (String generatedClass : classesTypeMap.keySet()) {
edgeClasses.add(generatedClass);
}
/**
* Inject the classes into bootstrap class loader by using Unsafe Strategy.
* ByteBuddy adapts the sun.misc.Unsafe and jdk.internal.misc.Unsafe automatically.
*/
ClassInjector.UsingUnsafe.Factory factory = ClassInjector.UsingUnsafe.Factory.resolve(instrumentation);
factory.make(null, null).injectRaw(classesTypeMap);
agentBuilder = agentBuilder.with(new AgentBuilder.InjectionStrategy.UsingUnsafe.OfFactory(factory));
return agentBuilder;
}
/**
* Get the delegate class name.
*
* @param methodsInterceptor of original interceptor in the plugin
* @return generated delegate class name
*/
public static String internalDelegate(String methodsInterceptor) {
return methodsInterceptor + "_internal";
}
/**
* Load the delegate class from current class loader, mostly should be AppClassLoader.
*
* @param methodsInterceptor of original interceptor in the plugin
* @return generated delegate class
*/
public static Class forInternalDelegateClass(String methodsInterceptor) {
try {
return Class.forName(internalDelegate(methodsInterceptor));
} catch (ClassNotFoundException e) {
throw new PluginException(e.getMessage(), e);
}
}
/**
* Generate dynamic delegate for ByteBuddy
*
* @param pluginFinder gets the whole plugin list.
* @param classesTypeMap hosts the class binary.
* @return true if have JRE instrumentation requirement.
* @throws PluginException when generate failure.
*/
private static boolean prepareJREInstrumentation(PluginFinder pluginFinder,
Map<String, byte[]> classesTypeMap) throws PluginException {
TypePool typePool = TypePool.Default.of(BootstrapInstrumentBoost.class.getClassLoader());
List<AbstractClassEnhancePluginDefine> bootstrapClassMatchDefines = pluginFinder.getBootstrapClassMatchDefine();
for (AbstractClassEnhancePluginDefine define : bootstrapClassMatchDefines) {
if (Objects.nonNull(define.getInstanceMethodsInterceptPoints())) {
for (InstanceMethodsInterceptPoint point : define.getInstanceMethodsInterceptPoints()) {
if (point.isOverrideArgs()) {
generateDelegator(
classesTypeMap, typePool, INSTANCE_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, point
.getMethodsInterceptor());
} else {
generateDelegator(
classesTypeMap, typePool, INSTANCE_METHOD_DELEGATE_TEMPLATE, point.getMethodsInterceptor());
}
}
}
if (Objects.nonNull(define.getConstructorsInterceptPoints())) {
for (ConstructorInterceptPoint point : define.getConstructorsInterceptPoints()) {
generateDelegator(
classesTypeMap, typePool, CONSTRUCTOR_DELEGATE_TEMPLATE, point.getConstructorInterceptor());
}
}
if (Objects.nonNull(define.getStaticMethodsInterceptPoints())) {
for (StaticMethodsInterceptPoint point : define.getStaticMethodsInterceptPoints()) {
if (point.isOverrideArgs()) {
generateDelegator(
classesTypeMap, typePool, STATIC_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, point
.getMethodsInterceptor());
} else {
generateDelegator(
classesTypeMap, typePool, STATIC_METHOD_DELEGATE_TEMPLATE, point.getMethodsInterceptor());
}
}
}
}
return bootstrapClassMatchDefines.size() > 0;
}
private static boolean prepareJREInstrumentationV2(PluginFinder pluginFinder,
Map<String, byte[]> classesTypeMap) throws PluginException {
TypePool typePool = TypePool.Default.of(BootstrapInstrumentBoost.class.getClassLoader());
List<AbstractClassEnhancePluginDefine> bootstrapClassMatchDefines = pluginFinder.getBootstrapClassMatchDefine();
for (AbstractClassEnhancePluginDefine define : bootstrapClassMatchDefines) {
if (Objects.nonNull(define.getInstanceMethodsInterceptV2Points())) {
for (InstanceMethodsInterceptV2Point point : define.getInstanceMethodsInterceptV2Points()) {
if (point.isOverrideArgs()) {
generateDelegator(classesTypeMap, typePool,
INSTANCE_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE,
point.getMethodsInterceptorV2());
} else {
generateDelegator(
classesTypeMap, typePool, INSTANCE_METHOD_V2_DELEGATE_TEMPLATE,
point.getMethodsInterceptorV2());
}
}
}
if (Objects.nonNull(define.getStaticMethodsInterceptV2Points())) {
for (StaticMethodsInterceptV2Point point : define.getStaticMethodsInterceptV2Points()) {
if (point.isOverrideArgs()) {
generateDelegator(classesTypeMap, typePool,
STATIC_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE,
point.getMethodsInterceptorV2());
} else {
generateDelegator(
classesTypeMap, typePool, STATIC_METHOD_V2_DELEGATE_TEMPLATE,
point.getMethodsInterceptorV2());
}
}
}
}
return bootstrapClassMatchDefines.size() > 0;
}
/**
* Generate the delegator class based on given template class. This is preparation stage level code generation.
* <p>
* One key step to avoid class confliction between AppClassLoader and BootstrapClassLoader
*
* @param classesTypeMap hosts injected binary of generated class
* @param typePool to generate new class
* @param templateClassName represents the class as template in this generation process. The templates are
* pre-defined in Hippo4j agent core.
*/
private static void generateDelegator(Map<String, byte[]> classesTypeMap, TypePool typePool,
String templateClassName, String methodsInterceptor) {
String internalInterceptorName = internalDelegate(methodsInterceptor);
try {
TypeDescription templateTypeDescription = typePool.describe(templateClassName).resolve();
DynamicType.Unloaded interceptorType = new ByteBuddy().redefine(templateTypeDescription, ClassFileLocator.ForClassLoader
.of(BootstrapInstrumentBoost.class.getClassLoader()))
.name(internalInterceptorName)
.field(named("TARGET_INTERCEPTOR"))
.value(methodsInterceptor)
.make();
classesTypeMap.put(internalInterceptorName, interceptorType.getBytes());
InstrumentDebuggingClass.INSTANCE.log(interceptorType);
} catch (Exception e) {
throw new PluginException("Generate Dynamic plugin failure", e);
}
}
/**
* The class loaded by this method means it only should be loaded once in Bootstrap classloader, when bootstrap
* instrumentation active by any plugin
*
* @param loadedTypeMap hosts all injected class
* @param className to load
*/
private static void loadHighPriorityClass(Map<String, byte[]> loadedTypeMap,
String className) throws PluginException {
byte[] enhancedInstanceClassFile;
try {
String classResourceName = className.replaceAll("\\.", "/") + ".class";
InputStream resourceAsStream = AgentClassLoader.getDefault().getResourceAsStream(classResourceName);
if (resourceAsStream == null) {
throw new PluginException("High priority class " + className + " not found.");
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
// read bytes from the input stream and store them in buffer
while ((len = resourceAsStream.read(buffer)) != -1) {
// write bytes from the buffer into output stream
os.write(buffer, 0, len);
}
enhancedInstanceClassFile = os.toByteArray();
} catch (IOException e) {
throw new PluginException(e.getMessage(), e);
}
loadedTypeMap.put(className, enhancedInstanceClassFile);
}
}

@ -0,0 +1,102 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin.bootstrap;
import cn.hippo4j.agent.core.logging.api.ILog;
import cn.hippo4j.agent.core.logging.api.LogManager;
/**
* The log bridge makes the ILog accessible inside bootstrap classloader, especially for internal interceptor.
*/
public class BootstrapPluginLogBridge implements IBootstrapLog {
public static IBootstrapLog getLogger(String clazz) {
return new BootstrapPluginLogBridge(clazz);
}
private final ILog logger;
private BootstrapPluginLogBridge(String clazz) {
logger = LogManager.getLogger(clazz);
}
@Override
public void info(String format) {
logger.info(format);
}
@Override
public void info(String format, Object... arguments) {
logger.info(format, arguments);
}
@Override
public void warn(String format, Object... arguments) {
logger.warn(format, arguments);
}
@Override
public void warn(Throwable e, String format, Object... arguments) {
logger.warn(e, format, arguments);
}
@Override
public void error(String format, Throwable e) {
logger.error(format, e);
}
@Override
public void error(Throwable e, String format, Object... arguments) {
logger.error(e, format, arguments);
}
@Override
public boolean isDebugEnable() {
return logger.isDebugEnable();
}
@Override
public boolean isInfoEnable() {
return logger.isInfoEnable();
}
@Override
public boolean isWarnEnable() {
return logger.isWarnEnable();
}
@Override
public boolean isErrorEnable() {
return logger.isErrorEnable();
}
@Override
public void debug(String format) {
logger.debug(format);
}
@Override
public void debug(String format, Object... arguments) {
logger.debug(format, arguments);
}
@Override
public void error(String format) {
logger.error(format);
}
}

@ -15,30 +15,38 @@
* limitations under the License.
*/
package cn.hippo4j.config.event;
import org.springframework.util.StringUtils;
package cn.hippo4j.agent.core.plugin.bootstrap;
/**
* Config data change event.
* The log interface used in bootstrap internal interceptors.
* <p>
* Never used in any plugin or tracing core.
*/
public class ConfigDataChangeEvent extends AbstractEvent {
public interface IBootstrapLog {
void info(String format);
void info(String format, Object... arguments);
void warn(String format, Object... arguments);
void warn(Throwable e, String format, Object... arguments);
void error(String format, Throwable e);
void error(Throwable e, String format, Object... arguments);
boolean isDebugEnable();
boolean isInfoEnable();
public final String tenantId;
boolean isWarnEnable();
public final String itemId;
boolean isErrorEnable();
public final String tpId;
void debug(String format);
public final long lastModifiedTs;
void debug(String format, Object... arguments);
public ConfigDataChangeEvent(String tenantId, String itemId, String tpId, Long gmtModified) {
if (StringUtils.isEmpty(tenantId) || StringUtils.isEmpty(itemId) || StringUtils.isEmpty(tpId)) {
throw new IllegalArgumentException("DataId is null or group is null");
}
this.tenantId = tenantId;
this.itemId = itemId;
this.tpId = tpId;
this.lastModifiedTs = gmtModified;
}
void error(String format);
}

@ -0,0 +1,79 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin.bootstrap.template;
import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.This;
public class ConstructorInterTemplate {
/**
* This field is never set in the template, but has value in the runtime.
*/
private static String TARGET_INTERCEPTOR;
private static InstanceConstructorInterceptor INTERCEPTOR;
private static IBootstrapLog LOGGER;
/**
* Intercept the target constructor.
*
* @param obj target class instance.
* @param allArguments all constructor arguments
*/
@RuntimeType
public static void intercept(@This Object obj, @AllArguments Object[] allArguments) {
try {
prepare();
EnhancedInstance targetObject = (EnhancedInstance) obj;
if (INTERCEPTOR == null) {
return;
}
INTERCEPTOR.onConstruct(targetObject, allArguments);
} catch (Throwable t) {
LOGGER.error("ConstructorInter failure.", t);
}
}
/**
* Prepare the context. Link to the agent core in AppClassLoader.
*/
private static void prepare() {
if (INTERCEPTOR == null) {
ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
if (loader != null) {
IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
if (logger != null) {
LOGGER = logger;
INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
}
} else {
LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
}
}
}
}

@ -0,0 +1,125 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin.bootstrap.template;
import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import net.bytebuddy.implementation.bind.annotation.This;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
public class InstanceMethodInterTemplate {
/**
* This field is never set in the template, but has value in the runtime.
*/
private static String TARGET_INTERCEPTOR;
private static InstanceMethodsAroundInterceptor INTERCEPTOR;
private static IBootstrapLog LOGGER;
/**
* Intercept the target instance method.
*
* @param obj target class instance.
* @param allArguments all method arguments
* @param method method description.
* @param zuper the origin call ref.
* @return the return value of target instance method.
* @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
* bug, if anything triggers this condition ).
*/
@RuntimeType
public static Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper,
@Origin Method method) throws Throwable {
EnhancedInstance targetObject = (EnhancedInstance) obj;
prepare();
MethodInterceptResult result = new MethodInterceptResult();
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);
}
} catch (Throwable t) {
if (LOGGER != null) {
LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
}
}
Object ret = null;
try {
if (!result.isContinue()) {
ret = result._ret();
} else {
ret = zuper.call();
}
} catch (Throwable t) {
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);
}
} catch (Throwable t2) {
if (LOGGER != null) {
LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
}
}
throw t;
} finally {
try {
if (INTERCEPTOR != null) {
ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);
}
} catch (Throwable t) {
if (LOGGER != null) {
LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
}
}
}
return ret;
}
/**
* Prepare the context. Link to the agent core in AppClassLoader.
*/
private static void prepare() {
if (INTERCEPTOR == null) {
ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
if (loader != null) {
IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
if (logger != null) {
LOGGER = logger;
INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
}
} else {
LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
}
}
}
}

@ -0,0 +1,125 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin.bootstrap.template;
import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Morph;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.This;
import java.lang.reflect.Method;
public class InstanceMethodInterWithOverrideArgsTemplate {
/**
* This field is never set in the template, but has value in the runtime.
*/
private static String TARGET_INTERCEPTOR;
private static InstanceMethodsAroundInterceptor INTERCEPTOR;
private static IBootstrapLog LOGGER;
/**
* Intercept the target instance method.
*
* @param obj target class instance.
* @param allArguments all method arguments
* @param method method description.
* @param zuper the origin call ref.
* @return the return value of target instance method.
* @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
* bug, if anything triggers this condition ).
*/
@RuntimeType
public static Object intercept(@This Object obj, @AllArguments Object[] allArguments, @Morph OverrideCallable zuper,
@Origin Method method) throws Throwable {
EnhancedInstance targetObject = (EnhancedInstance) obj;
prepare();
MethodInterceptResult result = new MethodInterceptResult();
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);
}
} catch (Throwable t) {
if (LOGGER != null) {
LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
}
}
Object ret = null;
try {
if (!result.isContinue()) {
ret = result._ret();
} else {
ret = zuper.call(allArguments);
}
} catch (Throwable t) {
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);
}
} catch (Throwable t2) {
if (LOGGER != null) {
LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
}
}
throw t;
} finally {
try {
if (INTERCEPTOR != null) {
ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);
}
} catch (Throwable t) {
if (LOGGER != null) {
LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
}
}
}
return ret;
}
/**
* Prepare the context. Link to the agent core in AppClassLoader.
*/
private static void prepare() {
if (INTERCEPTOR == null) {
ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
if (loader != null) {
IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
if (logger != null) {
LOGGER = logger;
INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
}
} else {
LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
}
}
}
}

@ -0,0 +1,114 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin.bootstrap.template;
import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
public class StaticMethodInterTemplate {
/**
* This field is never set in the template, but has value in the runtime.
*/
private static String TARGET_INTERCEPTOR;
private static StaticMethodsAroundInterceptor INTERCEPTOR;
private static IBootstrapLog LOGGER;
/**
* Intercept the target static method.
*
* @param clazz target class
* @param allArguments all method arguments
* @param method method description.
* @param zuper the origin call ref.
* @return the return value of target static method.
* @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
* bug, if anything triggers this condition ).
*/
@RuntimeType
public static Object intercept(@Origin Class<?> clazz, @AllArguments Object[] allArguments, @Origin Method method,
@SuperCall Callable<?> zuper) throws Throwable {
prepare();
MethodInterceptResult result = new MethodInterceptResult();
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), result);
}
} catch (Throwable t) {
LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
}
Object ret = null;
try {
if (!result.isContinue()) {
ret = result._ret();
} else {
ret = zuper.call();
}
} catch (Throwable t) {
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t);
}
} catch (Throwable t2) {
LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage());
}
throw t;
} finally {
try {
if (INTERCEPTOR != null) {
ret = INTERCEPTOR.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret);
}
} catch (Throwable t) {
LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage());
}
}
return ret;
}
/**
* Prepare the context. Link to the agent core in AppClassLoader.
*/
private static void prepare() {
if (INTERCEPTOR == null) {
ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
if (loader != null) {
IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
if (logger != null) {
LOGGER = logger;
INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
}
} else {
LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
}
}
}
}

@ -0,0 +1,114 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin.bootstrap.template;
import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Morph;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import java.lang.reflect.Method;
public class StaticMethodInterWithOverrideArgsTemplate {
/**
* This field is never set in the template, but has value in the runtime.
*/
private static String TARGET_INTERCEPTOR;
private static StaticMethodsAroundInterceptor INTERCEPTOR;
private static IBootstrapLog LOGGER;
/**
* Intercept the target static method.
*
* @param clazz target class
* @param allArguments all method arguments
* @param method method description.
* @param zuper the origin call ref.
* @return the return value of target static method.
* @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
* bug, if anything triggers this condition ).
*/
@RuntimeType
public static Object intercept(@Origin Class<?> clazz, @AllArguments Object[] allArguments, @Origin Method method,
@Morph OverrideCallable zuper) throws Throwable {
prepare();
MethodInterceptResult result = new MethodInterceptResult();
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), result);
}
} catch (Throwable t) {
LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
}
Object ret = null;
try {
if (!result.isContinue()) {
ret = result._ret();
} else {
ret = zuper.call(allArguments);
}
} catch (Throwable t) {
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t);
}
} catch (Throwable t2) {
LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage());
}
throw t;
} finally {
try {
if (INTERCEPTOR != null) {
ret = INTERCEPTOR.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret);
}
} catch (Throwable t) {
LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage());
}
}
return ret;
}
/**
* Prepare the context. Link to the agent core in AppClassLoader.
*/
private static void prepare() {
if (INTERCEPTOR == null) {
ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
if (loader != null) {
IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
if (logger != null) {
LOGGER = logger;
INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
}
} else {
LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
}
}
}
}

@ -0,0 +1,128 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin.bootstrap.template.v2;
import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import net.bytebuddy.implementation.bind.annotation.This;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
/**
* This class wouldn't be loaded in real env. This is a class template for dynamic class generation.
*/
public class InstanceMethodInterV2Template {
/**
* This field is never set in the template, but has value in the runtime.
*/
private static String TARGET_INTERCEPTOR;
private static InstanceMethodsAroundInterceptorV2 INTERCEPTOR;
private static IBootstrapLog LOGGER;
/**
* Intercept the target instance method.
*
* @param obj target class instance.
* @param allArguments all method arguments
* @param method method description.
* @param zuper the origin call ref.
* @return the return value of target instance method.
* @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
* bug, if anything triggers this condition ).
*/
@RuntimeType
public static Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper,
@Origin Method method) throws Throwable {
EnhancedInstance targetObject = (EnhancedInstance) obj;
prepare();
MethodInvocationContext context = new MethodInvocationContext();
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), context);
}
} catch (Throwable t) {
if (LOGGER != null) {
LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
}
}
Object ret = null;
try {
if (!context.isContinue()) {
ret = context._ret();
} else {
ret = zuper.call();
}
} catch (Throwable t) {
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t, context);
}
} catch (Throwable t2) {
if (LOGGER != null) {
LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
}
}
throw t;
} finally {
try {
if (INTERCEPTOR != null) {
ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret, context);
}
} catch (Throwable t) {
if (LOGGER != null) {
LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
}
}
}
return ret;
}
/**
* Prepare the context. Link to the agent core in AppClassLoader.
*/
private static void prepare() {
if (INTERCEPTOR == null) {
ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
if (loader != null) {
IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
if (logger != null) {
LOGGER = logger;
INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
}
} else {
LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
}
}
}
}

@ -0,0 +1,128 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin.bootstrap.template.v2;
import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Morph;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.This;
import java.lang.reflect.Method;
/**
* This class wouldn't be loaded in real env. This is a class template for dynamic class generation.
*/
public class InstanceMethodInterV2WithOverrideArgsTemplate {
/**
* This field is never set in the template, but has value in the runtime.
*/
private static String TARGET_INTERCEPTOR;
private static InstanceMethodsAroundInterceptorV2 INTERCEPTOR;
private static IBootstrapLog LOGGER;
/**
* Intercept the target instance method.
*
* @param obj target class instance.
* @param allArguments all method arguments
* @param method method description.
* @param zuper the origin call ref.
* @return the return value of target instance method.
* @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
* bug, if anything triggers this condition ).
*/
@RuntimeType
public static Object intercept(@This Object obj, @AllArguments Object[] allArguments, @Morph OverrideCallable zuper,
@Origin Method method) throws Throwable {
EnhancedInstance targetObject = (EnhancedInstance) obj;
prepare();
MethodInvocationContext context = new MethodInvocationContext();
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), context);
}
} catch (Throwable t) {
if (LOGGER != null) {
LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
}
}
Object ret = null;
try {
if (!context.isContinue()) {
ret = context._ret();
} else {
ret = zuper.call(allArguments);
}
} catch (Throwable t) {
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t, context);
}
} catch (Throwable t2) {
if (LOGGER != null) {
LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
}
}
throw t;
} finally {
try {
if (INTERCEPTOR != null) {
ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret, context);
}
} catch (Throwable t) {
if (LOGGER != null) {
LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
}
}
}
return ret;
}
/**
* Prepare the context. Link to the agent core in AppClassLoader.
*/
private static void prepare() {
if (INTERCEPTOR == null) {
ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
if (loader != null) {
IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
if (logger != null) {
LOGGER = logger;
INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
}
} else {
LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
}
}
}
}

@ -0,0 +1,117 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin.bootstrap.template.v2;
import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
/**
* This class wouldn't be loaded in real env. This is a class template for dynamic class generation.
*/
public class StaticMethodInterV2Template {
/**
* This field is never set in the template, but has value in the runtime.
*/
private static String TARGET_INTERCEPTOR;
private static StaticMethodsAroundInterceptorV2 INTERCEPTOR;
private static IBootstrapLog LOGGER;
/**
* Intercept the target static method.
*
* @param clazz target class
* @param allArguments all method arguments
* @param method method description.
* @param zuper the origin call ref.
* @return the return value of target static method.
* @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
* bug, if anything triggers this condition ).
*/
@RuntimeType
public static Object intercept(@Origin Class<?> clazz, @AllArguments Object[] allArguments, @Origin Method method,
@SuperCall Callable<?> zuper) throws Throwable {
prepare();
MethodInvocationContext context = new MethodInvocationContext();
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), context);
}
} catch (Throwable t) {
LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
}
Object ret = null;
try {
if (!context.isContinue()) {
ret = context._ret();
} else {
ret = zuper.call();
}
} catch (Throwable t) {
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t, context);
}
} catch (Throwable t2) {
LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage());
}
throw t;
} finally {
try {
if (INTERCEPTOR != null) {
ret = INTERCEPTOR.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret, context);
}
} catch (Throwable t) {
LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage());
}
}
return ret;
}
/**
* Prepare the context. Link to the agent core in AppClassLoader.
*/
private static void prepare() {
if (INTERCEPTOR == null) {
ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
if (loader != null) {
IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
if (logger != null) {
LOGGER = logger;
INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
}
} else {
LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
}
}
}
}

@ -0,0 +1,117 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin.bootstrap.template.v2;
import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Morph;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import java.lang.reflect.Method;
/**
* This class wouldn't be loaded in real env. This is a class template for dynamic class generation.
*/
public class StaticMethodInterV2WithOverrideArgsTemplate {
/**
* This field is never set in the template, but has value in the runtime.
*/
private static String TARGET_INTERCEPTOR;
private static StaticMethodsAroundInterceptorV2 INTERCEPTOR;
private static IBootstrapLog LOGGER;
/**
* Intercept the target static method.
*
* @param clazz target class
* @param allArguments all method arguments
* @param method method description.
* @param zuper the origin call ref.
* @return the return value of target static method.
* @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
* bug, if anything triggers this condition ).
*/
@RuntimeType
public static Object intercept(@Origin Class<?> clazz, @AllArguments Object[] allArguments, @Origin Method method,
@Morph OverrideCallable zuper) throws Throwable {
prepare();
MethodInvocationContext context = new MethodInvocationContext();
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), context);
}
} catch (Throwable t) {
LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
}
Object ret = null;
try {
if (!context.isContinue()) {
ret = context._ret();
} else {
ret = zuper.call(allArguments);
}
} catch (Throwable t) {
try {
if (INTERCEPTOR != null) {
INTERCEPTOR.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t, context);
}
} catch (Throwable t2) {
LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage());
}
throw t;
} finally {
try {
if (INTERCEPTOR != null) {
ret = INTERCEPTOR.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret, context);
}
} catch (Throwable t) {
LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage());
}
}
return ret;
}
/**
* Prepare the context. Link to the agent core in AppClassLoader.
*/
private static void prepare() {
if (INTERCEPTOR == null) {
ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
if (loader != null) {
IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
if (logger != null) {
LOGGER = logger;
INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
}
} else {
LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
}
}
}
}

@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.agent.core.plugin.bytebuddy;
import net.bytebuddy.matcher.ElementMatcher;
public abstract class AbstractJunction<V> implements ElementMatcher.Junction<V> {
@Override
public <U extends V> Junction<U> and(ElementMatcher<? super U> other) {
return new Conjunction<U>(this, other);
}
@Override
public <U extends V> Junction<U> or(ElementMatcher<? super U> other) {
return new Disjunction<U>(this, other);
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save