mirror of https://github.com/longtai-cn/hippo4j
Initialize the hippo4j-agent module and complete the construction of the agent-core core module (#1196) (#1201)
parent
9b33ea69c0
commit
254b587111
@ -0,0 +1,311 @@
|
|||||||
|
<?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/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>hippo4j-agent</artifactId>
|
||||||
|
<groupId>cn.hippo4j</groupId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>hippo4j-agent-core</artifactId>
|
||||||
|
<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 />
|
||||||
|
</transformers>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.bytebuddy</groupId>
|
||||||
|
<artifactId>byte-buddy</artifactId>
|
||||||
|
<version>1.12.13</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.bytebuddy</groupId>
|
||||||
|
<artifactId>byte-buddy-agent</artifactId>
|
||||||
|
<version>1.12.13</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.tomakehurst</groupId>
|
||||||
|
<artifactId>wiremock</artifactId>
|
||||||
|
<version>2.6.0</version>
|
||||||
|
<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>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>jetty-server</artifactId>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>jetty-servlet</artifactId>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>jetty-servlets</artifactId>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>jetty-webapp</artifactId>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>xmlunit-core</artifactId>
|
||||||
|
<groupId>org.xmlunit</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>xmlunit-legacy</artifactId>
|
||||||
|
<groupId>org.xmlunit</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>json-path</artifactId>
|
||||||
|
<groupId>com.jayway.jsonpath</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>jopt-simple</artifactId>
|
||||||
|
<groupId>net.sf.jopt-simple</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>zjsonpatch</artifactId>
|
||||||
|
<groupId>com.flipkart.zjsonpatch</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>handlebars</artifactId>
|
||||||
|
<groupId>com.github.jknack</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.stefanbirkner</groupId>
|
||||||
|
<artifactId>system-rules</artifactId>
|
||||||
|
<version>1.18.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>junit-dep</artifactId>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjdk.jmh</groupId>
|
||||||
|
<artifactId>jmh-generator-annprocess</artifactId>
|
||||||
|
<version>1.33</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.powermock</groupId>
|
||||||
|
<artifactId>powermock-api-mockito2</artifactId>
|
||||||
|
<version>2.0.7</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>powermock-api-support</artifactId>
|
||||||
|
<groupId>org.powermock</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.objenesis</groupId>
|
||||||
|
<artifactId>objenesis</artifactId>
|
||||||
|
<version>3.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjdk.jmh</groupId>
|
||||||
|
<artifactId>jmh-core</artifactId>
|
||||||
|
<version>1.33</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>commons-math3</artifactId>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>jopt-simple</artifactId>
|
||||||
|
<groupId>net.sf.jopt-simple</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.20</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.annotation</groupId>
|
||||||
|
<artifactId>javax.annotation-api</artifactId>
|
||||||
|
<version>1.3.2</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.powermock</groupId>
|
||||||
|
<artifactId>powermock-module-junit4</artifactId>
|
||||||
|
<version>2.0.7</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>powermock-module-junit4-common</artifactId>
|
||||||
|
<groupId>org.powermock</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>hamcrest-core</artifactId>
|
||||||
|
<groupId>org.hamcrest</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>${guava.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.bytebuddy</groupId>
|
||||||
|
<artifactId>byte-buddy</artifactId>
|
||||||
|
<version>${bytebuddy.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>${slf4j.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
<properties>
|
||||||
|
<shade.io.perfmark.target>${shade.package}.${shade.io.perfmark.source}</shade.io.perfmark.target>
|
||||||
|
<shade.io.opencensus.source>io.opencensus</shade.io.opencensus.source>
|
||||||
|
<shade.org.slf4j.target>${shade.package}.${shade.org.slf4j.source}</shade.org.slf4j.target>
|
||||||
|
<shade.io.grpc.target>${shade.package}.${shade.io.grpc.source}</shade.io.grpc.target>
|
||||||
|
<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.io.netty.source>io.netty</shade.io.netty.source>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<wiremock.version>2.6.0</wiremock.version>
|
||||||
|
<ststem-rules.version>1.18.0</ststem-rules.version>
|
||||||
|
<shade.io.opencensus.target>${shade.package}.${shade.io.opencensus.source}</shade.io.opencensus.target>
|
||||||
|
<slf4j.version>1.7.25</slf4j.version>
|
||||||
|
<shade.io.netty.target>${shade.package}.${shade.io.netty.source}</shade.io.netty.target>
|
||||||
|
<shade.com.google.source>com.google</shade.com.google.source>
|
||||||
|
<guava.version>30.1.1-jre</guava.version>
|
||||||
|
<shade.io.perfmark.source>io.perfmark</shade.io.perfmark.source>
|
||||||
|
<shade.org.slf4j.source>org.slf4j</shade.org.slf4j.source>
|
||||||
|
<shade.io.grpc.source>io.grpc</shade.io.grpc.source>
|
||||||
|
<shade.com.google.target>${shade.package}.${shade.com.google.source}</shade.com.google.target>
|
||||||
|
</properties>
|
||||||
|
</project>
|
@ -0,0 +1,188 @@
|
|||||||
|
<?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>
|
||||||
|
<guava.version>30.1.1-jre</guava.version>
|
||||||
|
<wiremock.version>2.6.0</wiremock.version>
|
||||||
|
<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>
|
||||||
|
<slf4j.version>1.7.25</slf4j.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.bytebuddy</groupId>
|
||||||
|
<artifactId>byte-buddy</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
<artifactId>gson</artifactId>
|
||||||
|
<version>${gson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.bytebuddy</groupId>
|
||||||
|
<artifactId>byte-buddy-agent</artifactId>
|
||||||
|
<version>${bytebuddy.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.tomakehurst</groupId>
|
||||||
|
<artifactId>wiremock</artifactId>
|
||||||
|
<version>${wiremock.version}</version>
|
||||||
|
<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>com.github.stefanbirkner</groupId>
|
||||||
|
<artifactId>system-rules</artifactId>
|
||||||
|
<version>${ststem-rules.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjdk.jmh</groupId>
|
||||||
|
<artifactId>jmh-generator-annprocess</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>${guava.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.bytebuddy</groupId>
|
||||||
|
<artifactId>byte-buddy</artifactId>
|
||||||
|
<version>${bytebuddy.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>${slf4j.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
<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 SkyWalking 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 = "SkywalkingAgent-" + 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 org.apache.skywalking.apm.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,143 @@
|
|||||||
|
/*
|
||||||
|
* 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.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,380 @@
|
|||||||
|
/*
|
||||||
|
* 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 = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication active is based on backend setting, see application.yml for more details. For most scenarios,
|
||||||
|
* this needs backend extensions, only basic match auth provided in default implementation.
|
||||||
|
*/
|
||||||
|
public static String AUTHENTICATION = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, SkyWalking agent will save all instrumented classes files in `/debugging` folder. SkyWalking team
|
||||||
|
* may ask for these files in order to resolve compatible problem.
|
||||||
|
*/
|
||||||
|
public static boolean IS_OPEN_DEBUGGING_CLASS = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, SkyWalking 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 SkyWalking 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identifier of the instance
|
||||||
|
*/
|
||||||
|
@Length(50)
|
||||||
|
public volatile static String INSTANCE_NAME = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* service instance properties in json format. e.g. agent.instance_properties_json = {"org":
|
||||||
|
* "apache-skywalking"}
|
||||||
|
*/
|
||||||
|
public static String INSTANCE_PROPERTIES_JSON = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How depth the agent goes, when log cause exceptions.
|
||||||
|
*/
|
||||||
|
public static int CAUSE_EXCEPTION_DEPTH = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force reconnection period of grpc, based on grpc_channel_check_interval. If count of check grpc channel
|
||||||
|
* status more than this number. The channel check will call channel.getState(true) to requestConnection.
|
||||||
|
*/
|
||||||
|
public static long FORCE_RECONNECTION_PERIOD = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit the length of the operationName to prevent the overlength issue in the storage.
|
||||||
|
*
|
||||||
|
* <p>NOTICE</p>
|
||||||
|
* In the current practice, we don't recommend the length over 190.
|
||||||
|
*/
|
||||||
|
public static int OPERATION_NAME_THRESHOLD = 150;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep tracing even the backend is not available.
|
||||||
|
*/
|
||||||
|
public static boolean KEEP_TRACING = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force open TLS for gRPC channel if true.
|
||||||
|
*/
|
||||||
|
public static boolean FORCE_TLS = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSL trusted ca file. If it exists, will enable TLS for gRPC channel.
|
||||||
|
*/
|
||||||
|
public static String SSL_TRUSTED_CA_PATH = "ca" + Constants.PATH_SEPARATOR + "ca.crt";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key cert chain file. If ssl_cert_chain and ssl_key exist, will enable mTLS for gRPC channel.
|
||||||
|
*/
|
||||||
|
public static String SSL_CERT_CHAIN_PATH;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private key file. If ssl_cert_chain and ssl_key exist, will enable mTLS for gRPC channel.
|
||||||
|
*/
|
||||||
|
public static String SSL_KEY_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class OsInfo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit the length of the ipv4 list size.
|
||||||
|
*/
|
||||||
|
public static int IPV4_LIST_SIZE = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Collector {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* grpc channel status check interval
|
||||||
|
*/
|
||||||
|
public static long GRPC_CHANNEL_CHECK_INTERVAL = 30;
|
||||||
|
/**
|
||||||
|
* The period in which the agent report a heartbeat to the backend.
|
||||||
|
*/
|
||||||
|
public static long HEARTBEAT_PERIOD = 30;
|
||||||
|
/**
|
||||||
|
* The agent sends the instance properties to the backend every `collector.heartbeat_period *
|
||||||
|
* collector.properties_report_period_factor` seconds
|
||||||
|
*/
|
||||||
|
public static int PROPERTIES_REPORT_PERIOD_FACTOR = 10;
|
||||||
|
/**
|
||||||
|
* Collector skywalking trace receiver service addresses.
|
||||||
|
*/
|
||||||
|
public static String BACKEND_SERVICE = "";
|
||||||
|
/**
|
||||||
|
* How long grpc client will timeout in sending data to upstream.
|
||||||
|
*/
|
||||||
|
public static int GRPC_UPSTREAM_TIMEOUT = 30;
|
||||||
|
/**
|
||||||
|
* Get profile task list interval
|
||||||
|
*/
|
||||||
|
public static int GET_PROFILE_TASK_INTERVAL = 20;
|
||||||
|
/**
|
||||||
|
* Get agent dynamic config interval
|
||||||
|
*/
|
||||||
|
public static int GET_AGENT_DYNAMIC_CONFIG_INTERVAL = 20;
|
||||||
|
/**
|
||||||
|
* If true, skywalking agent will enable periodically resolving DNS to update receiver service addresses.
|
||||||
|
*/
|
||||||
|
public static boolean IS_RESOLVE_DNS_PERIODICALLY = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Profile {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, skywalking agent will enable profile when user create a new profile task. Otherwise disable
|
||||||
|
* profile.
|
||||||
|
*/
|
||||||
|
public static boolean ACTIVE = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parallel monitor segment count
|
||||||
|
*/
|
||||||
|
public static int MAX_PARALLEL = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max monitor segment time(minutes), if current segment monitor time out of limit, then stop it.
|
||||||
|
*/
|
||||||
|
public static int MAX_DURATION = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max dump thread stack depth
|
||||||
|
*/
|
||||||
|
public static int DUMP_MAX_STACK_DEPTH = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Snapshot transport to backend buffer size
|
||||||
|
*/
|
||||||
|
public static int SNAPSHOT_TRANSPORT_BUFFER_SIZE = 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Meter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, skywalking agent will enable sending meters. Otherwise disable meter report.
|
||||||
|
*/
|
||||||
|
public static boolean ACTIVE = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report meters interval
|
||||||
|
*/
|
||||||
|
public static Integer REPORT_INTERVAL = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max size of the meter count.
|
||||||
|
*/
|
||||||
|
public static Integer MAX_METER_SIZE = 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Jvm {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The buffer size of collected JVM info.
|
||||||
|
*/
|
||||||
|
public static int BUFFER_SIZE = 60 * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Log {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The max size of message to send to server.Default is 10 MB.
|
||||||
|
*/
|
||||||
|
public static int MAX_MESSAGE_SIZE = 10 * 1024 * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Buffer {
|
||||||
|
|
||||||
|
public static int CHANNEL_SIZE = 5;
|
||||||
|
|
||||||
|
public static int BUFFER_SIZE = 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Logging {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log file name.
|
||||||
|
*/
|
||||||
|
public static String FILE_NAME = "skywalking-api.log";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log files directory. Default is blank string, means, use "{theSkywalkingAgentJarDir}/logs " to output logs.
|
||||||
|
* {theSkywalkingAgentJarDir} is the directory where the skywalking 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 StatusCheck {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listed exceptions would not be treated as an error. Because in some codes, the exception is being used as a
|
||||||
|
* way of controlling business flow.
|
||||||
|
*/
|
||||||
|
public static String IGNORED_EXCEPTIONS = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The max recursive depth when checking the exception traced by the agent. Typically, we don't recommend
|
||||||
|
* setting this more than 10, which could cause a performance issue. Negative value and 0 would be ignored,
|
||||||
|
* which means all exceptions would make the span tagged in error status.
|
||||||
|
*/
|
||||||
|
public static Integer MAX_RECURSIVE_DEPTH = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Correlation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max element count in the correlation context.
|
||||||
|
*/
|
||||||
|
public static int ELEMENT_MAX_NUMBER = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max value length of each element.
|
||||||
|
*/
|
||||||
|
public static int VALUE_MAX_LENGTH = 128;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tag the span by the key/value in the correlation context, when the keys listed here exist.
|
||||||
|
*/
|
||||||
|
public static String AUTO_TAG_KEYS = "";
|
||||||
|
}
|
||||||
|
}
|
@ -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,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.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;
|
||||||
|
}
|
@ -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,231 @@
|
|||||||
|
/*
|
||||||
|
* 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.*;
|
||||||
|
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 = "skywalking_config";
|
||||||
|
private static final String DEFAULT_CONFIG_FILE_NAME = "/config/agent.config";
|
||||||
|
private static final String ENV_KEY_PREFIX = "skywalking.";
|
||||||
|
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 `skywalking.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, skywalking 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 `skywalking`, the result should be as
|
||||||
|
* same as in `agent.config`
|
||||||
|
* <p>
|
||||||
|
* such as: Property key of `agent.service_name` should be `skywalking.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,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,216 @@
|
|||||||
|
/*
|
||||||
|
* 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.*;
|
||||||
|
|
||||||
|
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,230 @@
|
|||||||
|
/*
|
||||||
|
* 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.*;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
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 SkyWalking 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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public enum LogLevel {
|
||||||
|
TRACE, DEBUG, INFO, WARN, ERROR, OFF
|
||||||
|
}
|
@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The <code>LogMessageHolder</code> is a {@link String} holder, in order to in-process propagation String across the
|
||||||
|
* disruptor queue.
|
||||||
|
*/
|
||||||
|
public class LogMessageHolder {
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
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,61 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import static cn.hippo4j.agent.core.logging.core.LogOutput.FILE;
|
||||||
|
|
||||||
|
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,101 @@
|
|||||||
|
/*
|
||||||
|
* 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.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
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 = "org.apache.skywalking.apm.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 (skywalking-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,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;
|
||||||
|
|
||||||
|
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.*;
|
||||||
|
|
||||||
|
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 'skywalking-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("skywalking-plugin.def");
|
||||||
|
|
||||||
|
while (urls.hasMoreElements()) {
|
||||||
|
URL pluginUrl = urls.nextElement();
|
||||||
|
cfgUrlPaths.add(pluginUrl);
|
||||||
|
LOGGER.info("find skywalking 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,297 @@
|
|||||||
|
/*
|
||||||
|
* 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.*;
|
||||||
|
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 = {
|
||||||
|
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist",
|
||||||
|
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor",
|
||||||
|
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor",
|
||||||
|
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor",
|
||||||
|
"org.apache.skywalking.apm.agent.core.plugin.bootstrap.IBootstrapLog",
|
||||||
|
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance",
|
||||||
|
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.OverrideCallable",
|
||||||
|
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult",
|
||||||
|
|
||||||
|
// interceptor v2
|
||||||
|
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2",
|
||||||
|
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2",
|
||||||
|
"org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext",
|
||||||
|
};
|
||||||
|
|
||||||
|
private static String INSTANCE_METHOD_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.InstanceMethodInterTemplate";
|
||||||
|
private static String INSTANCE_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.InstanceMethodInterWithOverrideArgsTemplate";
|
||||||
|
private static String CONSTRUCTOR_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.ConstructorInterTemplate";
|
||||||
|
private static String STATIC_METHOD_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.StaticMethodInterTemplate";
|
||||||
|
private static String STATIC_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.StaticMethodInterWithOverrideArgsTemplate";
|
||||||
|
|
||||||
|
private static String INSTANCE_METHOD_V2_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.v2.InstanceMethodInterV2Template";
|
||||||
|
private static String INSTANCE_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.v2.InstanceMethodInterV2WithOverrideArgsTemplate";
|
||||||
|
private static String STATIC_METHOD_V2_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.template.v2.StaticMethodInterV2Template";
|
||||||
|
private static String STATIC_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "org.apache.skywalking.apm.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 SkyWalking 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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The log interface used in bootstrap internal interceptors.
|
||||||
|
* <p>
|
||||||
|
* Never used in any plugin or tracing core.
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
|
||||||
|
boolean isWarnEnable();
|
||||||
|
|
||||||
|
boolean isErrorEnable();
|
||||||
|
|
||||||
|
void debug(String format);
|
||||||
|
|
||||||
|
void debug(String format, Object... arguments);
|
||||||
|
|
||||||
|
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,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.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.*;
|
||||||
|
|
||||||
|
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,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;
|
||||||
|
|
||||||
|
import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
|
||||||
|
import cn.hippo4j.agent.core.plugin.interceptor.enhance.*;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.*;
|
||||||
|
|
||||||
|
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,123 @@
|
|||||||
|
/*
|
||||||
|
* 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.*;
|
||||||
|
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,124 @@
|
|||||||
|
/*
|
||||||
|
* 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.*;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cn.hippo4j.agent.core.plugin.bytebuddy;
|
||||||
|
|
||||||
|
import net.bytebuddy.description.annotation.AnnotationDescription;
|
||||||
|
import net.bytebuddy.description.annotation.AnnotationSource;
|
||||||
|
import net.bytebuddy.matcher.CollectionItemMatcher;
|
||||||
|
import net.bytebuddy.matcher.DeclaringAnnotationMatcher;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation Type match. Similar with {@link net.bytebuddy.matcher.ElementMatchers#isAnnotatedWith}, the only different
|
||||||
|
* between them is this match use {@link String} to declare the type, instead of {@link Class}. This can avoid the
|
||||||
|
* classloader risk.
|
||||||
|
* <p>
|
||||||
|
* 2019-08-15
|
||||||
|
*/
|
||||||
|
public class AnnotationTypeNameMatch<T extends AnnotationDescription> implements ElementMatcher<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the target annotation type
|
||||||
|
*/
|
||||||
|
private String annotationTypeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* declare the match target method with the certain type.
|
||||||
|
*
|
||||||
|
* @param annotationTypeName target annotation type
|
||||||
|
*/
|
||||||
|
private AnnotationTypeNameMatch(String annotationTypeName) {
|
||||||
|
this.annotationTypeName = annotationTypeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean matches(T target) {
|
||||||
|
return target.getAnnotationType().asErasure().getName().equals(annotationTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The static method to create {@link AnnotationTypeNameMatch} This is a delegate method to follow byte-buddy {@link
|
||||||
|
* ElementMatcher}'s code style.
|
||||||
|
*
|
||||||
|
* @param annotationTypeName target annotation type
|
||||||
|
* @param <T> The type of the object that is being matched.
|
||||||
|
* @return new {@link AnnotationTypeNameMatch} instance.
|
||||||
|
*/
|
||||||
|
public static <T extends AnnotationSource> Junction<T> isAnnotatedWithType(
|
||||||
|
String annotationTypeName) {
|
||||||
|
final AnnotationTypeNameMatch<AnnotationDescription> matcher = new AnnotationTypeNameMatch<AnnotationDescription>(annotationTypeName);
|
||||||
|
return new DeclaringAnnotationMatcher<T>(new CollectionItemMatcher<AnnotationDescription>(matcher));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* 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.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.method.ParameterList;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Argument Type match. Similar with {@link net.bytebuddy.matcher.ElementMatchers#takesArgument}, the only different
|
||||||
|
* between them is this match use {@link String} to declare the type, instead of {@link Class}. This can avoid the
|
||||||
|
* classloader risk.
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
public class ArgumentTypeNameMatch implements ElementMatcher<MethodDescription> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the index of arguments list.
|
||||||
|
*/
|
||||||
|
private int index;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the target argument type at {@link ArgumentTypeNameMatch#index} of the arguments list.
|
||||||
|
*/
|
||||||
|
private String argumentTypeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* declare the match target method with the certain index and type.
|
||||||
|
*
|
||||||
|
* @param index the index of arguments list.
|
||||||
|
* @param argumentTypeName target argument type
|
||||||
|
*/
|
||||||
|
private ArgumentTypeNameMatch(int index, String argumentTypeName) {
|
||||||
|
ArrayTypeNameChecker.check(argumentTypeName);
|
||||||
|
|
||||||
|
this.index = index;
|
||||||
|
this.argumentTypeName = argumentTypeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match the target method.
|
||||||
|
*
|
||||||
|
* @param target target method description.
|
||||||
|
* @return true if matched. or false.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean matches(MethodDescription target) {
|
||||||
|
ParameterList<?> parameters = target.getParameters();
|
||||||
|
if (parameters.size() > index) {
|
||||||
|
return parameters.get(index).getType().asErasure().getName().equals(argumentTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The static method to create {@link ArgumentTypeNameMatch} This is a delegate method to follow byte-buddy {@link
|
||||||
|
* ElementMatcher}'s code style.
|
||||||
|
*
|
||||||
|
* @param index the index of arguments list.
|
||||||
|
* @param argumentTypeName target argument type
|
||||||
|
* @return new {@link ArgumentTypeNameMatch} instance.
|
||||||
|
*/
|
||||||
|
public static ElementMatcher<MethodDescription> takesArgumentWithType(int index, String argumentTypeName) {
|
||||||
|
return new ArgumentTypeNameMatch(index, argumentTypeName);
|
||||||
|
}
|
||||||
|
}
|
@ -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.plugin.bytebuddy;
|
||||||
|
|
||||||
|
public class ArrayTypeNameChecker {
|
||||||
|
|
||||||
|
public static void check(String typeName) {
|
||||||
|
if (typeName.endsWith("[]")) {
|
||||||
|
throw new IllegalArgumentException("Please use [Lxxx; to define an Array type, and ref to JVM Specification for details");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* 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 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.util.FileUtils;
|
||||||
|
import cn.hippo4j.agent.core.util.IOUtils;
|
||||||
|
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||||
|
import net.bytebuddy.agent.builder.ResettableClassFileTransformer;
|
||||||
|
import net.bytebuddy.utility.RandomString;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.lang.instrument.IllegalClassFormatException;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper classFileTransformer of ByteBuddy, save the enhanced bytecode to memory cache or file cache,
|
||||||
|
* and automatically load the previously generated bytecode during the second retransform,
|
||||||
|
* to solve the problem that ByteBuddy generates auxiliary classes with different random names every time.
|
||||||
|
* Allow other javaagent to enhance those classes that enhanced by SkyWalking agent.
|
||||||
|
*/
|
||||||
|
public class CacheableTransformerDecorator implements AgentBuilder.TransformerDecorator {
|
||||||
|
|
||||||
|
private static final ILog LOGGER = LogManager.getLogger(CacheableTransformerDecorator.class);
|
||||||
|
|
||||||
|
private final ClassCacheMode cacheMode;
|
||||||
|
private ClassCacheResolver cacheResolver;
|
||||||
|
|
||||||
|
public CacheableTransformerDecorator(ClassCacheMode cacheMode) throws IOException {
|
||||||
|
this.cacheMode = cacheMode;
|
||||||
|
initClassCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initClassCache() throws IOException {
|
||||||
|
if (this.cacheMode.equals(ClassCacheMode.FILE)) {
|
||||||
|
String cacheDirBase = null;
|
||||||
|
try {
|
||||||
|
cacheDirBase = AgentPackagePath.getPath() + "/class-cache";
|
||||||
|
} catch (AgentPackageNotFoundException e) {
|
||||||
|
throw new IOException("Can't find the root path for creating /class-cache folder.");
|
||||||
|
}
|
||||||
|
File cacheDir = new File(cacheDirBase + "/class-cache-" + RandomString.make());
|
||||||
|
if (!cacheDir.exists()) {
|
||||||
|
cacheDir.mkdirs();
|
||||||
|
}
|
||||||
|
if (!cacheDir.exists()) {
|
||||||
|
throw new IOException("Create class cache dir failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheResolver = new FileCacheResolver(cacheDir);
|
||||||
|
} else {
|
||||||
|
cacheResolver = new MemoryCacheResolver();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResettableClassFileTransformer decorate(ResettableClassFileTransformer classFileTransformer) {
|
||||||
|
return new ResettableClassFileTransformer.WithDelegation(classFileTransformer) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
|
||||||
|
// load from cache
|
||||||
|
byte[] classCache = cacheResolver.getClassCache(loader, className);
|
||||||
|
if (classCache != null) {
|
||||||
|
return classCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
// transform class
|
||||||
|
classfileBuffer = classFileTransformer.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
|
||||||
|
|
||||||
|
// save to cache
|
||||||
|
if (classfileBuffer != null) {
|
||||||
|
cacheResolver.putClassCache(loader, className, classfileBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return classfileBuffer;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getClassLoaderHash(ClassLoader loader) {
|
||||||
|
String classloader;
|
||||||
|
if (loader != null) {
|
||||||
|
classloader = Integer.toHexString(loader.hashCode());
|
||||||
|
} else {
|
||||||
|
// classloader is null for BootstrapClassLoader
|
||||||
|
classloader = "00000000";
|
||||||
|
}
|
||||||
|
return classloader;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ClassCacheResolver {
|
||||||
|
|
||||||
|
byte[] getClassCache(ClassLoader loader, String className);
|
||||||
|
|
||||||
|
void putClassCache(ClassLoader loader, String className, byte[] classfileBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MemoryCacheResolver implements ClassCacheResolver {
|
||||||
|
|
||||||
|
// classloaderHashcode@className -> class bytes
|
||||||
|
private Map<String, byte[]> classCacheMap = new ConcurrentHashMap<String, byte[]>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getClassCache(ClassLoader loader, String className) {
|
||||||
|
String cacheKey = getCacheKey(loader, className);
|
||||||
|
return classCacheMap.get(cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putClassCache(ClassLoader loader, String className, byte[] classfileBuffer) {
|
||||||
|
String cacheKey = getCacheKey(loader, className);
|
||||||
|
classCacheMap.put(cacheKey, classfileBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCacheKey(ClassLoader loader, String className) {
|
||||||
|
return getClassLoaderHash(loader) + "@" + className;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class FileCacheResolver implements ClassCacheResolver {
|
||||||
|
|
||||||
|
private final File cacheDir;
|
||||||
|
|
||||||
|
FileCacheResolver(File cacheDir) {
|
||||||
|
this.cacheDir = cacheDir;
|
||||||
|
|
||||||
|
// clean cache dir on exit
|
||||||
|
FileUtils.deleteDirectoryOnExit(cacheDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getClassCache(ClassLoader loader, String className) {
|
||||||
|
// load from cache
|
||||||
|
File cacheFile = getCacheFile(loader, className);
|
||||||
|
if (cacheFile.exists()) {
|
||||||
|
FileInputStream fileInputStream = null;
|
||||||
|
try {
|
||||||
|
fileInputStream = new FileInputStream(cacheFile);
|
||||||
|
return IOUtils.toByteArray(fileInputStream);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.error("load class bytes from cache file failure", e);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(fileInputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putClassCache(ClassLoader loader, String className, byte[] classfileBuffer) {
|
||||||
|
File cacheFile = getCacheFile(loader, className);
|
||||||
|
cacheFile.getParentFile().mkdirs();
|
||||||
|
FileOutputStream output = null;
|
||||||
|
try {
|
||||||
|
output = new FileOutputStream(cacheFile);
|
||||||
|
IOUtils.copy(new ByteArrayInputStream(classfileBuffer), output);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.error("save class bytes to cache file failure", e);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getCacheFile(ClassLoader loader, String className) {
|
||||||
|
String filename = getClassLoaderHash(loader) + "/" + className.replace('.', '/') + ".class";
|
||||||
|
return new File(cacheDir, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -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.plugin.bytebuddy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ByteBuddy class cache mode
|
||||||
|
*/
|
||||||
|
public enum ClassCacheMode {
|
||||||
|
FILE, MEMORY
|
||||||
|
}
|
@ -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.bytebuddy;
|
||||||
|
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return Type match. Similar with {@link net.bytebuddy.matcher.ElementMatchers#returns}, the only different between
|
||||||
|
* them is this match use {@link String} to declare the type, instead of {@link Class}. This can avoid the classloader
|
||||||
|
* risk.
|
||||||
|
* <p>
|
||||||
|
* 2019-08-15
|
||||||
|
*/
|
||||||
|
public class ReturnTypeNameMatch implements ElementMatcher<MethodDescription> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the target return type
|
||||||
|
*/
|
||||||
|
private String returnTypeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* declare the match target method with the certain type.
|
||||||
|
*
|
||||||
|
* @param returnTypeName target return type
|
||||||
|
*/
|
||||||
|
private ReturnTypeNameMatch(String returnTypeName) {
|
||||||
|
ArrayTypeNameChecker.check(returnTypeName);
|
||||||
|
this.returnTypeName = returnTypeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean matches(MethodDescription target) {
|
||||||
|
return target.getReturnType().asErasure().getName().equals(returnTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The static method to create {@link ReturnTypeNameMatch} This is a delegate method to follow byte-buddy {@link
|
||||||
|
* ElementMatcher}'s code style.
|
||||||
|
*
|
||||||
|
* @param returnTypeName target return type
|
||||||
|
* @return new {@link ReturnTypeNameMatch} instance.
|
||||||
|
*/
|
||||||
|
public static ElementMatcher<MethodDescription> returnsWithType(String returnTypeName) {
|
||||||
|
return new ReturnTypeNameMatch(returnTypeName);
|
||||||
|
}
|
||||||
|
}
|
@ -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.plugin.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown to indicate that a illegal format plugin definition has been defined in skywalking-plugin.define.
|
||||||
|
*/
|
||||||
|
public class IllegalPluginDefineException extends Exception {
|
||||||
|
|
||||||
|
public IllegalPluginDefineException(String define) {
|
||||||
|
super("Illegal plugin define : " + define);
|
||||||
|
}
|
||||||
|
}
|
@ -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.interceptor;
|
||||||
|
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this
|
||||||
|
* "Intercept Point", the definition targets class's constructors, and the interceptor.
|
||||||
|
* <p>
|
||||||
|
* ref to two others: {@link StaticMethodsInterceptPoint} and {@link InstanceMethodsInterceptPoint}
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
public interface ConstructorInterceptPoint {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor matcher
|
||||||
|
*
|
||||||
|
* @return matcher instance.
|
||||||
|
*/
|
||||||
|
ElementMatcher<MethodDescription> getConstructorMatcher();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return represents a class name, the class instance must be a instance of {@link
|
||||||
|
* org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor}
|
||||||
|
*/
|
||||||
|
String getConstructorInterceptor();
|
||||||
|
}
|
@ -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.plugin.interceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this interface for those who only want to enhance declared method in case of some unexpected issue, such as spring
|
||||||
|
* controller
|
||||||
|
*/
|
||||||
|
public interface DeclaredInstanceMethodsInterceptPoint extends InstanceMethodsInterceptPoint {
|
||||||
|
}
|
@ -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.interceptor;
|
||||||
|
|
||||||
|
import cn.hippo4j.agent.core.plugin.PluginException;
|
||||||
|
|
||||||
|
public class EnhanceException extends PluginException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -2234782755784217255L;
|
||||||
|
|
||||||
|
public EnhanceException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnhanceException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
@ -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.interceptor;
|
||||||
|
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this
|
||||||
|
* "Intercept Point", the definition targets class's instance methods, and the interceptor.
|
||||||
|
* <p>
|
||||||
|
* ref to two others: {@link ConstructorInterceptPoint} and {@link StaticMethodsInterceptPoint}
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
public interface InstanceMethodsInterceptPoint {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class instance methods matcher.
|
||||||
|
*
|
||||||
|
* @return methods matcher
|
||||||
|
*/
|
||||||
|
ElementMatcher<MethodDescription> getMethodsMatcher();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return represents a class name, the class instance must instanceof InstanceMethodsAroundInterceptor.
|
||||||
|
*/
|
||||||
|
String getMethodsInterceptor();
|
||||||
|
|
||||||
|
boolean isOverrideArgs();
|
||||||
|
}
|
@ -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.interceptor;
|
||||||
|
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this
|
||||||
|
* "Intercept Point", the definition targets class's static methods, and the interceptor.
|
||||||
|
* <p>
|
||||||
|
* ref to two others: {@link ConstructorInterceptPoint} and {@link InstanceMethodsInterceptPoint}
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
public interface StaticMethodsInterceptPoint {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* static methods matcher.
|
||||||
|
*
|
||||||
|
* @return matcher instance.
|
||||||
|
*/
|
||||||
|
ElementMatcher<MethodDescription> getMethodsMatcher();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return represents a class name, the class instance must instanceof StaticMethodsAroundInterceptor.
|
||||||
|
*/
|
||||||
|
String getMethodsInterceptor();
|
||||||
|
|
||||||
|
boolean isOverrideArgs();
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 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.interceptor.enhance;
|
||||||
|
|
||||||
|
import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This assist help all bootstrap class core interceptor.
|
||||||
|
*/
|
||||||
|
public class BootstrapInterRuntimeAssist {
|
||||||
|
|
||||||
|
private static final String AGENT_CLASSLOADER_DEFAULT = "org.apache.skywalking.apm.agent.core.plugin.loader.AgentClassLoader";
|
||||||
|
private static final String DEFAULT_AGENT_CLASSLOADER_INSTANCE = "DEFAULT_LOADER";
|
||||||
|
private static final String LOG_MANAGER_CLASS = "org.apache.skywalking.apm.agent.core.plugin.bootstrap.BootstrapPluginLogBridge";
|
||||||
|
private static final String LOG_MANAGER_GET_LOGGER_METHOD = "getLogger";
|
||||||
|
private static final PrintStream OUT = System.out;
|
||||||
|
|
||||||
|
public static ClassLoader getAgentClassLoader() {
|
||||||
|
try {
|
||||||
|
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (loader == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Class<?> agentClassLoaderClass = Class.forName(AGENT_CLASSLOADER_DEFAULT, true, loader);
|
||||||
|
Field defaultLoaderField = agentClassLoaderClass.getDeclaredField(DEFAULT_AGENT_CLASSLOADER_INSTANCE);
|
||||||
|
defaultLoaderField.setAccessible(true);
|
||||||
|
ClassLoader defaultAgentClassLoader = (ClassLoader) defaultLoaderField.get(null);
|
||||||
|
|
||||||
|
return defaultAgentClassLoader;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace(OUT);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IBootstrapLog getLogger(ClassLoader defaultAgentClassLoader, String interceptor) {
|
||||||
|
try {
|
||||||
|
Class<?> logManagerClass = Class.forName(LOG_MANAGER_CLASS, true, defaultAgentClassLoader);
|
||||||
|
Method getLogger = logManagerClass.getMethod(LOG_MANAGER_GET_LOGGER_METHOD, String.class);
|
||||||
|
return (IBootstrapLog) getLogger.invoke(null, interceptor + "_internal");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace(OUT);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T createInterceptor(ClassLoader defaultAgentClassLoader, String className, IBootstrapLog log) {
|
||||||
|
try {
|
||||||
|
Class<?> interceptor = Class.forName(className, true, defaultAgentClassLoader);
|
||||||
|
return (T) interceptor.newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e, "Interceptor[{}] not found", className);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -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.plugin.interceptor.enhance;
|
||||||
|
|
||||||
|
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.PluginException;
|
||||||
|
import cn.hippo4j.agent.core.plugin.bootstrap.BootstrapInstrumentBoost;
|
||||||
|
import cn.hippo4j.agent.core.plugin.interceptor.*;
|
||||||
|
import cn.hippo4j.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point;
|
||||||
|
import cn.hippo4j.agent.core.plugin.interceptor.v2.StaticMethodsInterceptV2Point;
|
||||||
|
import cn.hippo4j.agent.core.util.StringUtil;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
|
import net.bytebuddy.implementation.FieldAccessor;
|
||||||
|
import net.bytebuddy.implementation.MethodDelegation;
|
||||||
|
import net.bytebuddy.implementation.SuperMethodCall;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.Morph;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
|
||||||
|
import static net.bytebuddy.jar.asm.Opcodes.ACC_PRIVATE;
|
||||||
|
import static net.bytebuddy.jar.asm.Opcodes.ACC_VOLATILE;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class controls all enhance operations, including enhance constructors, instance methods and static methods. All
|
||||||
|
* the enhances base on three types interceptor point: {@link ConstructorInterceptPoint}, {@link
|
||||||
|
* InstanceMethodsInterceptPoint} and {@link StaticMethodsInterceptPoint} If plugin is going to enhance constructors,
|
||||||
|
* instance methods, or both, {@link ClassEnhancePluginDefine} will add a field of {@link Object} type.
|
||||||
|
*/
|
||||||
|
public abstract class ClassEnhancePluginDefine extends AbstractClassEnhancePluginDefine {
|
||||||
|
|
||||||
|
private static final ILog LOGGER = LogManager.getLogger(ClassEnhancePluginDefine.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected DynamicType.Builder<?> enhanceInstance(TypeDescription typeDescription,
|
||||||
|
DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader,
|
||||||
|
EnhanceContext context) throws PluginException {
|
||||||
|
ConstructorInterceptPoint[] constructorInterceptPoints = getConstructorsInterceptPoints();
|
||||||
|
InstanceMethodsInterceptPoint[] instanceMethodsInterceptPoints = getInstanceMethodsInterceptPoints();
|
||||||
|
String enhanceOriginClassName = typeDescription.getTypeName();
|
||||||
|
boolean existedConstructorInterceptPoint = false;
|
||||||
|
if (constructorInterceptPoints != null && constructorInterceptPoints.length > 0) {
|
||||||
|
existedConstructorInterceptPoint = true;
|
||||||
|
}
|
||||||
|
boolean existedMethodsInterceptPoints = false;
|
||||||
|
if (instanceMethodsInterceptPoints != null && instanceMethodsInterceptPoints.length > 0) {
|
||||||
|
existedMethodsInterceptPoints = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nothing need to be enhanced in class instance, maybe need enhance static methods.
|
||||||
|
*/
|
||||||
|
if (!existedConstructorInterceptPoint && !existedMethodsInterceptPoints) {
|
||||||
|
return newClassBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manipulate class source code.<br/>
|
||||||
|
*
|
||||||
|
* new class need:<br/>
|
||||||
|
* 1.Add field, name {@link #CONTEXT_ATTR_NAME}.
|
||||||
|
* 2.Add a field accessor for this field.
|
||||||
|
*
|
||||||
|
* And make sure the source codes manipulation only occurs once.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
if (!typeDescription.isAssignableTo(EnhancedInstance.class)) {
|
||||||
|
if (!context.isObjectExtended()) {
|
||||||
|
newClassBuilder = newClassBuilder.defineField(
|
||||||
|
CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE)
|
||||||
|
.implement(EnhancedInstance.class)
|
||||||
|
.intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));
|
||||||
|
context.extendObjectCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2. enhance constructors
|
||||||
|
*/
|
||||||
|
if (existedConstructorInterceptPoint) {
|
||||||
|
for (ConstructorInterceptPoint constructorInterceptPoint : constructorInterceptPoints) {
|
||||||
|
if (isBootstrapInstrumentation()) {
|
||||||
|
newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher())
|
||||||
|
.intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration()
|
||||||
|
.to(BootstrapInstrumentBoost
|
||||||
|
.forInternalDelegateClass(constructorInterceptPoint
|
||||||
|
.getConstructorInterceptor()))));
|
||||||
|
} else {
|
||||||
|
newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher())
|
||||||
|
.intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration()
|
||||||
|
.to(new ConstructorInter(constructorInterceptPoint
|
||||||
|
.getConstructorInterceptor(), classLoader))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3. enhance instance methods
|
||||||
|
*/
|
||||||
|
if (existedMethodsInterceptPoints) {
|
||||||
|
for (InstanceMethodsInterceptPoint instanceMethodsInterceptPoint : instanceMethodsInterceptPoints) {
|
||||||
|
String interceptor = instanceMethodsInterceptPoint.getMethodsInterceptor();
|
||||||
|
if (StringUtil.isEmpty(interceptor)) {
|
||||||
|
throw new EnhanceException("no InstanceMethodsAroundInterceptor define to enhance class " + enhanceOriginClassName);
|
||||||
|
}
|
||||||
|
ElementMatcher.Junction<MethodDescription> junction = not(isStatic()).and(instanceMethodsInterceptPoint.getMethodsMatcher());
|
||||||
|
if (instanceMethodsInterceptPoint instanceof DeclaredInstanceMethodsInterceptPoint) {
|
||||||
|
junction = junction.and(ElementMatchers.<MethodDescription>isDeclaredBy(typeDescription));
|
||||||
|
}
|
||||||
|
if (instanceMethodsInterceptPoint.isOverrideArgs()) {
|
||||||
|
if (isBootstrapInstrumentation()) {
|
||||||
|
newClassBuilder = newClassBuilder.method(junction)
|
||||||
|
.intercept(MethodDelegation.withDefaultConfiguration()
|
||||||
|
.withBinders(Morph.Binder.install(OverrideCallable.class))
|
||||||
|
.to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
|
||||||
|
} else {
|
||||||
|
newClassBuilder = newClassBuilder.method(junction)
|
||||||
|
.intercept(MethodDelegation.withDefaultConfiguration()
|
||||||
|
.withBinders(Morph.Binder.install(OverrideCallable.class))
|
||||||
|
.to(new InstMethodsInterWithOverrideArgs(interceptor, classLoader)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isBootstrapInstrumentation()) {
|
||||||
|
newClassBuilder = newClassBuilder.method(junction)
|
||||||
|
.intercept(MethodDelegation.withDefaultConfiguration()
|
||||||
|
.to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
|
||||||
|
} else {
|
||||||
|
newClassBuilder = newClassBuilder.method(junction)
|
||||||
|
.intercept(MethodDelegation.withDefaultConfiguration()
|
||||||
|
.to(new InstMethodsInter(interceptor, classLoader)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newClassBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected DynamicType.Builder<?> enhanceClass(TypeDescription typeDescription, DynamicType.Builder<?> newClassBuilder,
|
||||||
|
ClassLoader classLoader) throws PluginException {
|
||||||
|
StaticMethodsInterceptPoint[] staticMethodsInterceptPoints = getStaticMethodsInterceptPoints();
|
||||||
|
String enhanceOriginClassName = typeDescription.getTypeName();
|
||||||
|
if (staticMethodsInterceptPoints == null || staticMethodsInterceptPoints.length == 0) {
|
||||||
|
return newClassBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (StaticMethodsInterceptPoint staticMethodsInterceptPoint : staticMethodsInterceptPoints) {
|
||||||
|
String interceptor = staticMethodsInterceptPoint.getMethodsInterceptor();
|
||||||
|
if (StringUtil.isEmpty(interceptor)) {
|
||||||
|
throw new EnhanceException("no StaticMethodsAroundInterceptor define to enhance class " + enhanceOriginClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (staticMethodsInterceptPoint.isOverrideArgs()) {
|
||||||
|
if (isBootstrapInstrumentation()) {
|
||||||
|
newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
|
||||||
|
.intercept(MethodDelegation.withDefaultConfiguration()
|
||||||
|
.withBinders(Morph.Binder.install(OverrideCallable.class))
|
||||||
|
.to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
|
||||||
|
} else {
|
||||||
|
newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
|
||||||
|
.intercept(MethodDelegation.withDefaultConfiguration()
|
||||||
|
.withBinders(Morph.Binder.install(OverrideCallable.class))
|
||||||
|
.to(new StaticMethodsInterWithOverrideArgs(interceptor)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isBootstrapInstrumentation()) {
|
||||||
|
newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
|
||||||
|
.intercept(MethodDelegation.withDefaultConfiguration()
|
||||||
|
.to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
|
||||||
|
} else {
|
||||||
|
newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
|
||||||
|
.intercept(MethodDelegation.withDefaultConfiguration()
|
||||||
|
.to(new StaticMethodsInter(interceptor)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return newClassBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return null, means enhance no v2 instance methods.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return null, means enhance no v2 static methods.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public StaticMethodsInterceptV2Point[] getStaticMethodsInterceptV2Points() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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.interceptor.enhance;
|
||||||
|
|
||||||
|
import cn.hippo4j.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugins, which only need enhance class instance methods. Actually, inherit from {@link
|
||||||
|
* ClassInstanceMethodsEnhancePluginDefine} has no differences with inherit from {@link ClassEnhancePluginDefine}. Just
|
||||||
|
* override {@link ClassEnhancePluginDefine#getStaticMethodsInterceptPoints}, and return NULL, which means nothing to
|
||||||
|
* enhance.
|
||||||
|
*/
|
||||||
|
public abstract class ClassInstanceMethodsEnhancePluginDefine extends ClassEnhancePluginDefine {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return null, means enhance no static methods.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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.interceptor.enhance;
|
||||||
|
|
||||||
|
import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
|
||||||
|
import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugins, which only need enhance class static methods. Actually, inherit from {@link
|
||||||
|
* ClassStaticMethodsEnhancePluginDefine} has no differences with inherit from {@link ClassEnhancePluginDefine}. Just
|
||||||
|
* override {@link ClassEnhancePluginDefine#getConstructorsInterceptPoints} and {@link
|
||||||
|
* ClassEnhancePluginDefine#getInstanceMethodsInterceptPoints}, and return NULL, which means nothing to enhance.
|
||||||
|
*/
|
||||||
|
public abstract class ClassStaticMethodsEnhancePluginDefine extends ClassEnhancePluginDefine {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return null, means enhance no constructors.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return null, means enhance no instance methods.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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.interceptor.enhance;
|
||||||
|
|
||||||
|
import cn.hippo4j.agent.core.logging.api.ILog;
|
||||||
|
import cn.hippo4j.agent.core.logging.api.LogManager;
|
||||||
|
import cn.hippo4j.agent.core.plugin.PluginException;
|
||||||
|
import cn.hippo4j.agent.core.plugin.loader.InterceptorInstanceLoader;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.AllArguments;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.This;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual byte-buddy's interceptor to intercept constructor methods. In this class, it provides a bridge between
|
||||||
|
* byte-buddy and sky-walking plugin.
|
||||||
|
*/
|
||||||
|
public class ConstructorInter {
|
||||||
|
|
||||||
|
private static final ILog LOGGER = LogManager.getLogger(ConstructorInter.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link InstanceConstructorInterceptor} This name should only stay in {@link String}, the real {@link Class}
|
||||||
|
* type will trigger classloader failure. If you want to know more, please check on books about Classloader or
|
||||||
|
* Classloader appointment mechanism.
|
||||||
|
*/
|
||||||
|
private InstanceConstructorInterceptor interceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param constructorInterceptorClassName class full name.
|
||||||
|
*/
|
||||||
|
public ConstructorInter(String constructorInterceptorClassName, ClassLoader classLoader) throws PluginException {
|
||||||
|
try {
|
||||||
|
interceptor = InterceptorInstanceLoader.load(constructorInterceptorClassName, classLoader);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
throw new PluginException("Can't create InstanceConstructorInterceptorV2.", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intercept the target constructor.
|
||||||
|
*
|
||||||
|
* @param obj target class instance.
|
||||||
|
* @param allArguments all constructor arguments
|
||||||
|
*/
|
||||||
|
@RuntimeType
|
||||||
|
public void intercept(@This Object obj, @AllArguments Object[] allArguments) {
|
||||||
|
try {
|
||||||
|
EnhancedInstance targetObject = (EnhancedInstance) obj;
|
||||||
|
|
||||||
|
interceptor.onConstruct(targetObject, allArguments);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
LOGGER.error("ConstructorInter failure.", t);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -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.plugin.interceptor.enhance;
|
||||||
|
|
||||||
|
public interface EnhancedInstance {
|
||||||
|
|
||||||
|
Object getSkyWalkingDynamicField();
|
||||||
|
|
||||||
|
void setSkyWalkingDynamicField(Object value);
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* 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.interceptor.enhance;
|
||||||
|
|
||||||
|
import cn.hippo4j.agent.core.logging.api.ILog;
|
||||||
|
import cn.hippo4j.agent.core.logging.api.LogManager;
|
||||||
|
import cn.hippo4j.agent.core.plugin.PluginException;
|
||||||
|
import cn.hippo4j.agent.core.plugin.loader.InterceptorInstanceLoader;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between
|
||||||
|
* byte-buddy and sky-walking plugin.
|
||||||
|
*/
|
||||||
|
public class InstMethodsInter {
|
||||||
|
|
||||||
|
private static final ILog LOGGER = LogManager.getLogger(InstMethodsInter.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link InstanceMethodsAroundInterceptor} This name should only stay in {@link String}, the real {@link Class}
|
||||||
|
* type will trigger classloader failure. If you want to know more, please check on books about Classloader or
|
||||||
|
* Classloader appointment mechanism.
|
||||||
|
*/
|
||||||
|
private InstanceMethodsAroundInterceptor interceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param instanceMethodsAroundInterceptorClassName class full name.
|
||||||
|
*/
|
||||||
|
public InstMethodsInter(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) {
|
||||||
|
try {
|
||||||
|
interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
throw new PluginException("Can't create InstanceMethodsAroundInterceptor.", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper,
|
||||||
|
@Origin Method method) throws Throwable {
|
||||||
|
EnhancedInstance targetObject = (EnhancedInstance) obj;
|
||||||
|
|
||||||
|
MethodInterceptResult result = new MethodInterceptResult();
|
||||||
|
try {
|
||||||
|
interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
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 {
|
||||||
|
interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);
|
||||||
|
} catch (Throwable t2) {
|
||||||
|
LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
|
||||||
|
}
|
||||||
|
throw t;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* 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.interceptor.enhance;
|
||||||
|
|
||||||
|
import cn.hippo4j.agent.core.logging.api.ILog;
|
||||||
|
import cn.hippo4j.agent.core.logging.api.LogManager;
|
||||||
|
import cn.hippo4j.agent.core.plugin.PluginException;
|
||||||
|
import cn.hippo4j.agent.core.plugin.loader.InterceptorInstanceLoader;
|
||||||
|
import net.bytebuddy.implementation.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between
|
||||||
|
* byte-buddy and sky-walking plugin.
|
||||||
|
*/
|
||||||
|
public class InstMethodsInterWithOverrideArgs {
|
||||||
|
|
||||||
|
private static final ILog LOGGER = LogManager.getLogger(InstMethodsInterWithOverrideArgs.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link InstanceMethodsAroundInterceptor} This name should only stay in {@link String}, the real {@link Class}
|
||||||
|
* type will trigger classloader failure. If you want to know more, please check on books about Classloader or
|
||||||
|
* Classloader appointment mechanism.
|
||||||
|
*/
|
||||||
|
private InstanceMethodsAroundInterceptor interceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param instanceMethodsAroundInterceptorClassName class full name.
|
||||||
|
*/
|
||||||
|
public InstMethodsInterWithOverrideArgs(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) {
|
||||||
|
try {
|
||||||
|
interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
throw new PluginException("Can't create InstanceMethodsAroundInterceptor.", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 Object intercept(@This Object obj, @AllArguments Object[] allArguments, @Origin Method method,
|
||||||
|
@Morph OverrideCallable zuper) throws Throwable {
|
||||||
|
EnhancedInstance targetObject = (EnhancedInstance) obj;
|
||||||
|
|
||||||
|
MethodInterceptResult result = new MethodInterceptResult();
|
||||||
|
try {
|
||||||
|
interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
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 {
|
||||||
|
interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);
|
||||||
|
} catch (Throwable t2) {
|
||||||
|
LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
|
||||||
|
}
|
||||||
|
throw t;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
@ -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.interceptor.enhance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The instance constructor's interceptor interface. Any plugin, which wants to intercept constructor, must implement
|
||||||
|
* this interface.
|
||||||
|
* <p>
|
||||||
|
*/
|
||||||
|
public interface InstanceConstructorInterceptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after the origin constructor invocation.
|
||||||
|
*/
|
||||||
|
void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable;
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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.interceptor.enhance;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A interceptor, which intercept method's invocation. The target methods will be defined in {@link
|
||||||
|
* ClassEnhancePluginDefine}'s subclass, most likely in {@link ClassInstanceMethodsEnhancePluginDefine}
|
||||||
|
*/
|
||||||
|
public interface InstanceMethodsAroundInterceptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called before target method invocation.
|
||||||
|
*
|
||||||
|
* @param result change this result, if you want to truncate the method.
|
||||||
|
*/
|
||||||
|
void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
|
||||||
|
MethodInterceptResult result) throws Throwable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called after target method invocation. Even method's invocation triggers an exception.
|
||||||
|
*
|
||||||
|
* @param ret the method's original return value. May be null if the method triggers an exception.
|
||||||
|
* @return the method's actual return value.
|
||||||
|
*/
|
||||||
|
Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
|
||||||
|
Object ret) throws Throwable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called when occur exception.
|
||||||
|
*
|
||||||
|
* @param t the exception occur.
|
||||||
|
*/
|
||||||
|
void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
|
||||||
|
Class<?>[] argumentsTypes, Throwable t);
|
||||||
|
}
|
@ -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.plugin.interceptor.enhance;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a method return value manipulator. When a interceptor's method, such as {@link
|
||||||
|
* InstanceMethodsAroundInterceptor#beforeMethod(EnhancedInstance, Method, Object[], Class[], MethodInterceptResult)}
|
||||||
|
* (org.apache.skywalking.apm.agent.core.plugin.interceptor.EnhancedClassInstanceContext, has this as a method argument,
|
||||||
|
* the interceptor can manipulate the method's return value. <p> The new value set to this object, by {@link
|
||||||
|
* MethodInterceptResult#defineReturnValue(Object)}, will override the origin return value.
|
||||||
|
*/
|
||||||
|
public class MethodInterceptResult {
|
||||||
|
|
||||||
|
private boolean isContinue = true;
|
||||||
|
|
||||||
|
private Object ret = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* define the new return value.
|
||||||
|
*
|
||||||
|
* @param ret new return value.
|
||||||
|
*/
|
||||||
|
public void defineReturnValue(Object ret) {
|
||||||
|
this.isContinue = false;
|
||||||
|
this.ret = ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true, will trigger method interceptor({@link InstMethodsInter} and {@link StaticMethodsInter}) to invoke
|
||||||
|
* the origin method. Otherwise, not.
|
||||||
|
*/
|
||||||
|
public boolean isContinue() {
|
||||||
|
return isContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the new return value.
|
||||||
|
*/
|
||||||
|
public Object _ret() {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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.interceptor.enhance;
|
||||||
|
|
||||||
|
public interface OverrideCallable {
|
||||||
|
|
||||||
|
Object call(Object[] args);
|
||||||
|
}
|
@ -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.interceptor.enhance;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The static method's interceptor interface. Any plugin, which wants to intercept static methods, must implement this
|
||||||
|
* interface.
|
||||||
|
*/
|
||||||
|
public interface StaticMethodsAroundInterceptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called before target method invocation.
|
||||||
|
*
|
||||||
|
* @param result change this result, if you want to truncate the method.
|
||||||
|
*/
|
||||||
|
void beforeMethod(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes,
|
||||||
|
MethodInterceptResult result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called after target method invocation. Even method's invocation triggers an exception.
|
||||||
|
*
|
||||||
|
* @param ret the method's original return value.
|
||||||
|
* @return the method's actual return value.
|
||||||
|
*/
|
||||||
|
Object afterMethod(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes, Object ret);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called when occur exception.
|
||||||
|
*
|
||||||
|
* @param t the exception occur.
|
||||||
|
*/
|
||||||
|
void handleMethodException(Class clazz, Method method, Object[] allArguments, Class<?>[] parameterTypes,
|
||||||
|
Throwable t);
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue