diff --git a/README.md b/README.md index cbd0b895..b791abe4 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,9 @@ A lightweight & opening Java Agent for Cloud-Native and APM system * `Jedis 3.5.x`、 `Lettuce 5.3.x (sync、async)` * `ElasticSearch Client >= 7.x (sync、async)` * `Mongodb Client >=4.0.x (sync、async)` - * `Motan >= 1.2.0` + * `Motan` + * `Dubbo` + * `SofaRpc >= 5.3.0` * Collecting Access Logs. * `HTTP Servlet`、`HTTP Filter` * `Spring Cloud Gateway` diff --git a/build/src/main/resources/agent.properties b/build/src/main/resources/agent.properties index 798abb30..d25ae84e 100644 --- a/build/src/main/resources/agent.properties +++ b/build/src/main/resources/agent.properties @@ -262,6 +262,19 @@ plugin.observability.motan.metric.url=/platform-metrics #plugin.observability.dubbo.metric.url=/platform-metrics # plugin.observability.dubbo.metric.appendType=kafka +# -------------------- sofarpc --------------------- +# sofarpc tracing +# plugin.observability.sofarpc.tracing.enabled=true +## sofarpc args collect switch +# plugin.observability.sofarpc.tracing.args.collect.enabled=false +## sofarpc result collect switch +# plugin.observability.sofarpc.tracing.result.collect.enabled=false +# sofarpc metric +# plugin.observability.sofarpc.metric.enabled=true +# plugin.observability.sofarpc.metric.interval=30 +plugin.observability.sofarpc.metric.topic=platform-metrics +plugin.observability.sofarpc.metric.url=/platform-metrics +# plugin.observability.sofarpc.metric.appendType=kafka # -------------- output ------------------ ## http/kafka/zipkin server host and port for tracing and metric diff --git a/doc/prometheus-metric-schedule.md b/doc/prometheus-metric-schedule.md index f4775a83..7f87bf13 100644 --- a/doc/prometheus-metric-schedule.md +++ b/doc/prometheus-metric-schedule.md @@ -116,38 +116,6 @@ JDBC Connection schema describes key metrics of Getting Connection, which includ | application_jdbc_connection_p99 | double | TP99: The JDBC connection establishment duration in milliseconds for 99% user. | | application_jdbc_connection_p999 | double | TP99.9: The JDBC connection establishment duration in milliseconds for 99.9% user. | -### Motan -Motan schema describes key metrics of Motan client invoking, which include: -* Total execution count (cnt, errcnt, m1cnt, m5cnt, m15cnt) -* Throughput (m1, m5, m15, mean_rate) -* Error throughput (m1err, m5err, m15err) -* Execution duration (min, mean, max) -* Latency (p25, p50, p75, p95, p98, p99, p999) - -| Metric Name | Type | Description | -|:------------------------------------| :-----: |:----------------------------------------------------------------------------------------------------------| -| application_motan_cnt | integer | The total count of the Motan method executed | -| application_motan_errcnt | integer | The total error count of the Motan method executed | -| application_motan_m1cnt | integer | The total count of the Motan method executed in last 1 minute | -| application_motan_m5cnt | integer | The total count of the Motan method executed in last 5 minute | -| application_motan_m15cnt | integer | The total count of the Motan method executed in last 15 minute | -| application_motan_m1 | double | The Motan method executions per second (exponentially-weighted moving average) in last 1 minute | -| application_motan_m5 | double | The Motan method executions per second (exponentially-weighted moving average) in last 5 minute. | -| application_motan_m15 | double | The Motan method executions per second (exponentially-weighted moving average) in last 15 minute. | -| application_motan_mean_rate | double | The Motan method executions per second (exponentially-weighted moving average) in last 15 minute. | -| application_motan_m1err | double | The Motan method error executions per second (exponentially-weighted moving average) in last 1 minute | -| application_motan_m5err | double | The Motan method error executions per second (exponentially-weighted moving average) in last 5 minute. | -| application_motan_m15err | double | The Motan method error executions per second (exponentially-weighted moving average) in last 15 minute | -| application_motan_min | double | The Motan method minimal execution duration in milliseconds. | -| application_motan_max | double | The Motan method maximal execution duration in milliseconds. | -| application_motan_mean | double | The Motan method mean execution duration in milliseconds. | -| application_motan_p25 | double | TP25: The Motan method execution duration in milliseconds for 25% user. | -| application_motan_p50 | double | TP50: The Motan method execution duration in milliseconds for 50% user. | -| application_motan_p75 | double | TP75: The Motan method execution duration in milliseconds for 75% user. | -| application_motan_p95 | double | TP95: The Motan method execution duration in milliseconds for 95% user. | -| application_motan_p98 | double | TP98: The Motan method execution duration in milliseconds for 98% user. | -| application_motan_p99 | double | TP99: The Motan method execution duration in milliseconds for 99% user. | -| application_motan_p999 | double | TP999: The Motan method execution duration in milliseconds for 99.9% user. | #### Dedicated label | Label Name | Essential | Value Example | Description | @@ -415,6 +383,51 @@ MongoDB schema describes key metrics of MongoDB client invoking, which include: | application_mongodbclient_p98 | double | TP98: The MongoDB execution duration in milliseconds for 98% user. | | application_mongodbclient_p99 | double | TP99: The MongoDB execution duration in milliseconds for 99% user. | +#### Dedicated label +| Label Name | Essential | Value Example | Description | +|:-----------|:---------:|:-------------:|:---------------------------------------------------------------------------------------------------------------------------------------| +| operation | true | `insert` | The MongoDB request command name: insert, update or find etc. Reference: [Command](https://docs.mongodb.com/manual/reference/command/) | + + +### Motan +Motan schema describes key metrics of Motan client invoking, which include: +* Total execution count (cnt, errcnt, m1cnt, m5cnt, m15cnt) +* Throughput (m1, m5, m15, mean_rate) +* Error throughput (m1err, m5err, m15err) +* Execution duration (min, mean, max) +* Latency (p25, p50, p75, p95, p98, p99, p999) + +| Metric Name | Type | Description | +|:------------------------------------| :-----: |:----------------------------------------------------------------------------------------------------------| +| application_motan_cnt | integer | The total count of the Motan method executed | +| application_motan_errcnt | integer | The total error count of the Motan method executed | +| application_motan_m1cnt | integer | The total count of the Motan method executed in last 1 minute | +| application_motan_m5cnt | integer | The total count of the Motan method executed in last 5 minute | +| application_motan_m15cnt | integer | The total count of the Motan method executed in last 15 minute | +| application_motan_m1 | double | The Motan method executions per second (exponentially-weighted moving average) in last 1 minute | +| application_motan_m5 | double | The Motan method executions per second (exponentially-weighted moving average) in last 5 minute. | +| application_motan_m15 | double | The Motan method executions per second (exponentially-weighted moving average) in last 15 minute. | +| application_motan_mean_rate | double | The Motan method executions per second (exponentially-weighted moving average) in last 15 minute. | +| application_motan_m1err | double | The Motan method error executions per second (exponentially-weighted moving average) in last 1 minute | +| application_motan_m5err | double | The Motan method error executions per second (exponentially-weighted moving average) in last 5 minute. | +| application_motan_m15err | double | The Motan method error executions per second (exponentially-weighted moving average) in last 15 minute | +| application_motan_min | double | The Motan method minimal execution duration in milliseconds. | +| application_motan_max | double | The Motan method maximal execution duration in milliseconds. | +| application_motan_mean | double | The Motan method mean execution duration in milliseconds. | +| application_motan_p25 | double | TP25: The Motan method execution duration in milliseconds for 25% user. | +| application_motan_p50 | double | TP50: The Motan method execution duration in milliseconds for 50% user. | +| application_motan_p75 | double | TP75: The Motan method execution duration in milliseconds for 75% user. | +| application_motan_p95 | double | TP95: The Motan method execution duration in milliseconds for 95% user. | +| application_motan_p98 | double | TP98: The Motan method execution duration in milliseconds for 98% user. | +| application_motan_p99 | double | TP99: The Motan method execution duration in milliseconds for 99% user. | +| application_motan_p999 | double | TP999: The Motan method execution duration in milliseconds for 99.9% user. | + +#### Dedicated label +| Label Name | Essential | Value Example | Description | +|:-----------|:---------:|:------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| interface | true | `com.megaease.easeagent.motan.api.TestService/sayHi(String)` | The full signature of the `Motan` call interface, like this `com.megaease.easyagent.motan.api.TestService/sayHi(String)`. [Details](./user-manual.md#tracing-and-metric) | + + ### Dubbo Dubbo schema describes key metrics of Dubbo client invoking, which include: * Total execution count (cnt, errcnt, m1cnt, m5cnt, m15cnt) @@ -449,6 +462,45 @@ Dubbo schema describes key metrics of Dubbo client invoking, which include: | application_dubbo_p999 | double | TP999: The Dubbo method execution duration in milliseconds for 99.9% user. | #### Dedicated label -| Label Name | Essential | Value Example | Description | -|:-----------|:---------:|:-------------:|:---------------------------------------------------------------------------------------------------------------------------------------| -| operation | true | `insert` | The MongoDB request command name: insert, update or find etc. Reference: [Command](https://docs.mongodb.com/manual/reference/command/) | +| Label Name | Essential | Value Example | Description | +|:-----------|:---------:|:------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| interface | true | `com.megaease.easeagent.dubbo.api.TestService/sayHi(String)` | The full signature of the `Dubbo` call interface, like this `com.megaease.easyagent.dubbo.api.TestService/sayHi(String)`. [Details](./user-manual.md#tracing-and-metric) | + + +### SOFARPC +SOFARPC schema describes key metrics of SOFARPC client invoking, which include: +* Total execution count (cnt, errcnt, m1cnt, m5cnt, m15cnt) +* Throughput (m1, m5, m15, mean_rate) +* Error throughput (m1err, m5err, m15err) +* Execution duration (min, mean, max) +* Latency (p25, p50, p75, p95, p98, p99, p999) + +| Metric Name | Type | Description | +|:------------------------------------| :-----: |:---------------------------------------------------------------------------------------------------------| +| application_sofarpc_cnt | integer | The total count of the SOFARPC method executed | +| application_sofarpc_errcnt | integer | The total error count of the SOFARPC method executed | +| application_sofarpc_m1cnt | integer | The total count of the SOFARPC method executed in last 1 minute | +| application_sofarpc_m5cnt | integer | The total count of the SOFARPC method executed in last 5 minute | +| application_sofarpc_m15cnt | integer | The total count of the SOFARPC method executed in last 15 minute | +| application_sofarpc_m1 | double | The SOFARPC method executions per second (exponentially-weighted moving average) in last 1 minute | +| application_sofarpc_m5 | double | The SOFARPC method executions per second (exponentially-weighted moving average) in last 5 minute. | +| application_sofarpc_m15 | double | The SOFARPC method executions per second (exponentially-weighted moving average) in last 15 minute. | +| application_sofarpc_mean_rate | double | The SOFARPC method executions per second (exponentially-weighted moving average) in last 15 minute. | +| application_sofarpc_m1err | double | The SOFARPC method error executions per second (exponentially-weighted moving average) in last 1 minute | +| application_sofarpc_m5err | double | The SOFARPC method error executions per second (exponentially-weighted moving average) in last 5 minute. | +| application_sofarpc_m15err | double | The SOFARPC method error executions per second (exponentially-weighted moving average) in last 15 minute | +| application_sofarpc_min | double | The SOFARPC method minimal execution duration in milliseconds. | +| application_sofarpc_max | double | The SOFARPC method maximal execution duration in milliseconds. | +| application_sofarpc_mean | double | The SOFARPC method mean execution duration in milliseconds. | +| application_sofarpc_p25 | double | TP25: The SOFARPC method execution duration in milliseconds for 25% user. | +| application_sofarpc_p50 | double | TP50: The SOFARPC method execution duration in milliseconds for 50% user. | +| application_sofarpc_p75 | double | TP75: The SOFARPC method execution duration in milliseconds for 75% user. | +| application_sofarpc_p95 | double | TP95: The SOFARPC method execution duration in milliseconds for 95% user. | +| application_sofarpc_p98 | double | TP98: The SOFARPC method execution duration in milliseconds for 98% user. | +| application_sofarpc_p99 | double | TP99: The SOFARPC method execution duration in milliseconds for 99% user. | +| application_sofarpc_p999 | double | TP999: The SOFARPC method execution duration in milliseconds for 99.9% user. | + +#### Dedicated label +| Label Name | Essential | Value Example | Description | +|:-----------|:---------:|:------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| interface | true | `com.megaease.easeagent.sofarpc.api.TestService/sayHi(String)` | The full signature of the `SOFARPC` call interface, like this `com.megaease.easyagent.sofarpc.api.TestService/sayHi(String)`. [Details](./user-manual.md#tracing-and-metric) | diff --git a/doc/user-manual.md b/doc/user-manual.md index a5d64905..18d9cffe 100644 --- a/doc/user-manual.md +++ b/doc/user-manual.md @@ -6,7 +6,7 @@ - [Getting the configuration file](#getting-the-configuration-file) - [Global Configuration](#global-configuration) - [Internal HTTP Server](#internal-http-server) - - [Output Data Server: Kafka and HTTP/Zipkin Server](#output-data-server-kafka-and-httpzipkin-server) + - [Output Data Server: Kafka and HTTP/Zipkin Server](#output-data-server:-kafka-and-httpzipkin-server) - [Progress Configuration](#progress-configuration) - [Forwarded headers config](#forwarded-headers-config) - [Tracing config](#tracing-config) @@ -148,7 +148,7 @@ In the process of supporting easemesh traffic coloring, the request header `X-Me |_____> servcieB-canary(X-Mesh-Canary=lv1) ``` -plugin enabled config: [Enabled](#Forwarded headers plugin enabled) +plugin enabled config: [Enabled](#forwarded-headers-plugin-enabled) ##### Tracing config @@ -246,6 +246,7 @@ Supported components and corresponding namespaces: | JVM Memory | `jvmMemory` | JVM Memory Metric | | dubbo | `dubbo` | dubbo Metric | | motan | `motan` | Motan Metric | +| sofarpc | `sofarpc` | SOFARPC Metric | #### Application Log Application log modules collecting application logs printed by the application. @@ -405,15 +406,23 @@ Response Body: EaseAgent use [brave](https://github.com/openzipkin/brave) to collect tracing logs.The data format stored in `Kafka` is [Zipkin Data Model](https://zipkin.io/pages/data_model.html). User can send tracing logs to [Zipkin server](https://zipkin.io/pages/quickstart.html). ### Tracing Component -| Component Type | Component | Reference | -| -------------- |---------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| HTTP Client | `RestTemplate`、 `WebClient`、 `FeignClient` | [brave-instrumentation-http](https://github.com/openzipkin/brave/tree/master/instrumentation/http) | -| HTTP Server | `Servlet`、`Filter` | [brave-instrumentation-http](https://github.com/openzipkin/brave/tree/master/instrumentation/http) | -| DataBase | `JDBC` | [Brave](https://github.com/openzipkin/brave/tree/master/brave) | -| Cache | `Jedis`、`Lettuce` | [Brave](https://github.com/openzipkin/brave/tree/master/brave) | -| Message | `RabbitMQ`、`Kafka` | [brave-instrumentation-messaging](https://github.com/openzipkin/brave/tree/master/instrumentation/messaging) 、[Brave Kafka instrumentation](https://github.com/openzipkin/brave/tree/master/instrumentation/kafka-clients) | -| Logging | `Log4j2`、`Logback` | [brave-context-log4j2](https://github.com/openzipkin/brave/tree/master/context/log4j2) 、[brave-context-slf4j](https://github.com/openzipkin/brave/tree/master/context/slf4j) | -| RPC | `AlibabaDubbo`、`ApacheDubbo`、`Motan` | [brave-instrumentation-dubbo](https://github.com/openzipkin/brave/tree/master/instrumentation/dubbo) 、[brave-instrumentation-dubbo-rpc](https://github.com/openzipkin/brave/tree/master/instrumentation/dubbo-rpc)、[brave-instrumentation-rpc](https://github.com/openzipkin/brave/tree/master/instrumentation/rpc) | +| Component Type | Component | Reference | +| -------------- |-------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| HTTP Client | `RestTemplate`、 `WebClient`、 `FeignClient` | [brave-instrumentation-http](https://github.com/openzipkin/brave/tree/master/instrumentation/http) | +| HTTP Server | `Servlet`、`Filter` | [brave-instrumentation-http](https://github.com/openzipkin/brave/tree/master/instrumentation/http) | +| DataBase | `JDBC` | [Brave](https://github.com/openzipkin/brave/tree/master/brave) | +| Cache | `Jedis`、`Lettuce` | [Brave](https://github.com/openzipkin/brave/tree/master/brave) | +| Message | `RabbitMQ`、`Kafka` | [brave-instrumentation-messaging](https://github.com/openzipkin/brave/tree/master/instrumentation/messaging) 、[Brave Kafka instrumentation](https://github.com/openzipkin/brave/tree/master/instrumentation/kafka-clients) | +| Logging | `Log4j2`、`Logback` | [brave-context-log4j2](https://github.com/openzipkin/brave/tree/master/context/log4j2) 、[brave-context-slf4j](https://github.com/openzipkin/brave/tree/master/context/slf4j) | +| RPC | `AlibabaDubbo`、`ApacheDubbo`、`Motan`,`SOFARPC` | [brave-instrumentation-dubbo](https://github.com/openzipkin/brave/tree/master/instrumentation/dubbo) 、[brave-instrumentation-dubbo-rpc](https://github.com/openzipkin/brave/tree/master/instrumentation/dubbo-rpc)、[brave-instrumentation-rpc](https://github.com/openzipkin/brave/tree/master/instrumentation/rpc) | + +### Tracing Component Config Description +| Component Type | Component | Description | +| -------------- |-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| RPC | `Motan` | The motan plugin adds a dynamic switch to collect interface parameters and return values for troubleshooting purposes. The display format is json (Notes: The library used is [Jackson](https://github.com/FasterXML/jackson.git)), which is disabled by default. You can enable the parameter collection switch with this configuration: `plugin.observability.motan.tracing.args.collect.enabled=ture`, and the return values collection switch with this configuration: `plugin.observability.motan.tracing.result.collect.enabled=ture`. | +| RPC | `Dubbo` | The motan plugin adds a dynamic switch to collect interface parameters and return values for troubleshooting purposes. The display format is json (Notes: The library used is [Jackson](https://github.com/FasterXML/jackson.git)), which is disabled by default. You can enable the parameter collection switch with this configuration: `plugin.observability.dubbo.tracing.args.collect.enabled=ture`, and the return values collection switch with this configuration: `plugin.observability.dubbo.tracing.result.collect.enabled=ture`. | +| RPC | `SOFARPC` | The motan plugin adds a dynamic switch to collect interface parameters and return values for troubleshooting purposes. The display format is json (Notes: The library used is [Jackson](https://github.com/FasterXML/jackson.git)), which is disabled by default. You can enable the parameter collection switch with this configuration: `plugin.observability.sofarpc.tracing.args.collect.enabled=ture`, and the return values collection switch with this configuration: `plugin.observability.sofarpc.tracing.result.collect.enabled=ture`. | + ### Custom Span Tag @@ -469,6 +478,16 @@ EaseAgent use [brave](https://github.com/openzipkin/brave) to collect tracing lo | motan.module | motan server module name | | motan.group | motan client group name | +#### SOFARPC Client and Server +| Tag | Description | +|-----------------------------|--------------------------------------| +| sofarpc.args | SOFARPC interface arguments | +| sofarpc.result | SOFARPC interface return value | +| sofarpc.service | SOFARPC service interface full name | +| sofarpc.method | SOFARPC service method signature | +| sofarpc.service.uniqueId | SOFARPC service interface uniqueId | +| sofarpc.client.application | SOFARPC client application name | +| sofarpc.server.application | SOFARPC server module name | ## Metric EaseAgent use [io.dropwizard.metrics](https://github.com/dropwizard/metrics) to collect metric information. @@ -848,7 +867,7 @@ Dubbo schema describes key metrics of Dubbo client invoking, which include: | Field | Type | Description | |:----------| :-----: |:-------------------------------------------------------------------------------------------------------| -| service | string | Dubbo method signature. | +| interface | string | Dubbo full method signature. | | cnt | integer | The total count of the Dubbo method executed | | errcnt | integer | The total error count of the Dubbo method executed | | m1cnt | integer | The total count of the Dubbo method executed in last 1 minute | @@ -882,7 +901,7 @@ Motan schema describes key metrics of Motan client invoking, which include: | Field | Type | Description | |:----------| :-----: |:-------------------------------------------------------------------------------------------------------| -| service | string | Motan method signature. | +| interface | string | Motan full method signature. | | cnt | integer | The total count of the Motan method executed | | errcnt | integer | The total error count of the Motan method executed | | m1cnt | integer | The total count of the Motan method executed in last 1 minute | @@ -906,5 +925,39 @@ Motan schema describes key metrics of Motan client invoking, which include: | p99 | double | TP99: The Motan method execution duration in milliseconds for 99% user. | | p999 | double | TP999: The Motan method execution duration in milliseconds for 99.9% user. | +#### SOFARPC +SOFARPC schema describes key metrics of SOFARPC client invoking, which include: +* Total execution count (cnt, errcnt, m1cnt, m5cnt, m15cnt) +* Throughput (m1, m5, m15, mean_rate) +* Error throughput (m1err, m5err, m15err) +* Execution duration (min, mean, max) +* Latency (p25, p50, p75, p95, p98, p99, p999) + +| Field | Type | Description | +|:----------| :-----: |:-------------------------------------------------------------------------------------------------------| +| interface | string | SOFARPC method signature. | +| cnt | integer | The total count of the SOFARPC method executed | +| errcnt | integer | The total error count of the SOFARPC method executed | +| m1cnt | integer | The total count of the SOFARPC method executed in last 1 minute | +| m5cnt | integer | The total count of the SOFARPC method executed in last 5 minute | +| m15cnt | integer | The total count of the SOFARPC method executed in last 15 minute | +| m1 | double | The SOFARPC method executions per second (exponentially-weighted moving average) in last 1 minute | +| m5 | double | The SOFARPC method executions per second (exponentially-weighted moving average) in last 5 minute. | +| m15 | double | The SOFARPC method executions per second (exponentially-weighted moving average) in last 15 minute. | +| mean_rate | double | The SOFARPC method executions per second (exponentially-weighted moving average) in last 15 minute. | +| m1err | double | The SOFARPC method error executions per second (exponentially-weighted moving average) in last 1 minute | +| m5err | double | The SOFARPC method error executions per second (exponentially-weighted moving average) in last 5 minute. | +| m15err | double | The SOFARPC method error executions per second (exponentially-weighted moving average) in last 15 minute | +| min | double | The SOFARPC method minimal execution duration in milliseconds. | +| max | double | The SOFARPC method maximal execution duration in milliseconds. | +| mean | double | The SOFARPC method mean execution duration in milliseconds. | +| p25 | double | TP25: The SOFARPC method execution duration in milliseconds for 25% user. | +| p50 | double | TP50: The SOFARPC method execution duration in milliseconds for 50% user. | +| p75 | double | TP75: The SOFARPC method execution duration in milliseconds for 75% user. | +| p95 | double | TP95: The SOFARPC method execution duration in milliseconds for 95% user. | +| p98 | double | TP98: The SOFARPC method execution duration in milliseconds for 98% user. | +| p99 | double | TP99: The SOFARPC method execution duration in milliseconds for 99% user. | +| p999 | double | TP999: The SOFARPC method execution duration in milliseconds for 99.9% user. | + ## Application Log diff --git a/plugin-api/src/main/java/com/megaease/easeagent/plugin/api/config/ConfigConst.java b/plugin-api/src/main/java/com/megaease/easeagent/plugin/api/config/ConfigConst.java index 59da2feb..7addef69 100644 --- a/plugin-api/src/main/java/com/megaease/easeagent/plugin/api/config/ConfigConst.java +++ b/plugin-api/src/main/java/com/megaease/easeagent/plugin/api/config/ConfigConst.java @@ -150,6 +150,7 @@ interface Namespace { // ------------ rpc ---------------------- String DUBBO = "dubbo"; String MOTAN = "motan"; + String SOFARPC = "sofarpc"; // ------------- request ------------------ diff --git a/plugin-api/src/main/java/com/megaease/easeagent/plugin/api/context/ContextUtils.java b/plugin-api/src/main/java/com/megaease/easeagent/plugin/api/context/ContextUtils.java index 7c67a71c..d919dc9f 100644 --- a/plugin-api/src/main/java/com/megaease/easeagent/plugin/api/context/ContextUtils.java +++ b/plugin-api/src/main/java/com/megaease/easeagent/plugin/api/context/ContextUtils.java @@ -60,4 +60,15 @@ public static Long getDuration(Context context, Object startKey) { public static T getFromContext(Context context, Object key) { return context.get(key); } + + /** + * Remove data from context + * @param context data store + * @param key key is the type of data. + * @return + * @param the type of data + */ + public static T removeFromContext(Context context, Object key) { + return context.remove(key); + } } diff --git a/plugins/pom.xml b/plugins/pom.xml index 13391ef7..bcb9e2fa 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -51,6 +51,7 @@ log4j2-log-plugin dubbo motan + sofarpc diff --git a/plugins/sofarpc/pom.xml b/plugins/sofarpc/pom.xml new file mode 100644 index 00000000..fd79d470 --- /dev/null +++ b/plugins/sofarpc/pom.xml @@ -0,0 +1,51 @@ + + + + plugins + com.megaease.easeagent + 2.2.5 + + 4.0.0 + + sofarpc + + + 8 + 8 + UTF-8 + 5.3.0 + + + + + com.megaease.easeagent + plugin-api + provided + + + com.google.code.findbugs + jsr305 + compile + + + com.megaease.easeagent + plugin-api-mock + ${project.version} + test + + + + com.alipay.sofa + sofa-rpc-all + ${sofa-rpc-all.version} + provided + + + com.megaease.easeagent + log4j2-api + + + + \ No newline at end of file diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/SofaRpcCtxUtils.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/SofaRpcCtxUtils.java new file mode 100644 index 00000000..0b593882 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/SofaRpcCtxUtils.java @@ -0,0 +1,307 @@ +package com.megaease.easeagent.plugin.sofarpc; + +import com.alipay.sofa.rpc.common.RpcConstants; +import com.alipay.sofa.rpc.core.request.SofaRequest; +import com.alipay.sofa.rpc.core.response.SofaResponse; +import com.alipay.sofa.rpc.filter.ConsumerInvoker; +import com.alipay.sofa.rpc.filter.ProviderInvoker; +import com.megaease.easeagent.plugin.api.Cleaner; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.api.config.ConfigConst; +import com.megaease.easeagent.plugin.api.context.AsyncContext; +import com.megaease.easeagent.plugin.api.context.ContextUtils; +import com.megaease.easeagent.plugin.api.context.RequestContext; +import com.megaease.easeagent.plugin.api.logging.Logger; +import com.megaease.easeagent.plugin.api.trace.Scope; +import com.megaease.easeagent.plugin.api.trace.Span; +import com.megaease.easeagent.plugin.bridge.EaseAgent; +import com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.SofaRpcMetricsBaseInterceptor; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.SofaRpcTraceBaseInterceptor; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.common.SofaClientTraceRequest; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.common.SofaServerTraceRequest; +import com.megaease.easeagent.plugin.utils.SystemClock; +import com.megaease.easeagent.plugin.utils.common.JsonUtil; + +public class SofaRpcCtxUtils { + private static final Logger LOG = EaseAgent.getLogger(SofaRpcCtxUtils.class); + public static final String METRICS_KEY_NAME = SofaRpcCtxUtils.class.getName() + ".METRICS_KEY_NAME"; + private static final String METRICS_IS_ASYNC = SofaRpcCtxUtils.class.getName() + ".METRICS_IS_ASYNC"; + private static final String TRACE_IS_ASYNC = SofaRpcCtxUtils.class.getName() + ".TRACE_IS_ASYNC"; + private static final String BEGIN_TIME = SofaRpcCtxUtils.class.getName() + ".BEGIN_TIME"; + + public static final String CLIENT_REQUEST_CONTEXT_KEY = SofaRpcCtxUtils.class.getName() + ".CLIENT_REQUEST_CONTEXT"; + public static final String SERVER_REQUEST_CONTEXT_KEY = SofaRpcCtxUtils.class.getName() + ".SERVER_REQUEST_CONTEXT"; + + /** + * SOFARPC span name, e.g. TestService/test(String) + * + * @param sofaRequest sofa rpc request + * @return + */ + public static String name(SofaRequest sofaRequest) { + StringBuilder operationName = new StringBuilder(); + operationName.append(sofaRequest.getMethod().getDeclaringClass().getSimpleName()); + operationName.append("/").append(sofaRequest.getMethod().getName()).append("("); + for (Class parameterType : sofaRequest.getMethod().getParameterTypes()) { + operationName.append(parameterType.getSimpleName()).append(","); + } + + if (sofaRequest.getMethod().getParameterTypes().length > 0) { + operationName.deleteCharAt(operationName.length() - 1); + } + + operationName.append(")"); + + return operationName.toString(); + } + + /** + * Format method name. e.g. test(String) + * + * @param sofaRequest + * @return method name + */ + public static String method(SofaRequest sofaRequest) { + StringBuilder methodName = new StringBuilder(); + methodName.append(sofaRequest.getMethod().getName()) + .append("("); + for (Class parameterType : sofaRequest.getMethod().getParameterTypes()) { + methodName.append(parameterType.getSimpleName()).append(","); + } + + if (sofaRequest.getMethod().getParameterTypes().length > 0) { + methodName.deleteCharAt(methodName.length() - 1); + } + methodName.append(")"); + return methodName.toString(); + } + + /** + * sofarpc interface signature, e.g. com.test.TestService/test(java.lang.String) + * + * @param sofaRequest + * @return + */ + public static String methodSignature(SofaRequest sofaRequest) { + StringBuilder operationName = new StringBuilder(); + operationName.append(sofaRequest.getMethod().getDeclaringClass().getName()); + operationName.append("/").append(sofaRequest.getMethod().getName()).append("("); + for (Class parameterType : sofaRequest.getMethod().getParameterTypes()) { + operationName.append(parameterType.getSimpleName()).append(","); + } + + if (sofaRequest.getMethod().getParameterTypes().length > 0) { + operationName.deleteCharAt(operationName.length() - 1); + } + + operationName.append(")"); + + return operationName.toString(); + } + + //-------------------Sofa Rpc trace operate method------------ + + /** + * Start client span + * @param context + * @param sofaRequest + * @param consumerInvoker + */ + public static void startClientSpan(Context context, SofaRequest sofaRequest, ConsumerInvoker consumerInvoker) { + SofaClientTraceRequest sofaClientTraceRequest = new SofaClientTraceRequest(consumerInvoker, sofaRequest); + RequestContext requestContext = context.clientRequest(sofaClientTraceRequest); + + Span span = requestContext.span().start(); + span.kind(sofaClientTraceRequest.kind()); + span.name(sofaClientTraceRequest.name()); + span.remoteServiceName(ConfigConst.Namespace.SOFARPC); + span.remoteIpAndPort(sofaClientTraceRequest.remoteHost(), sofaClientTraceRequest.remotePort()); + if (SofaRpcTraceBaseInterceptor.SOFA_RPC_TRACE_CONFIG.argsCollectEnabled()) { + span.tag(SofaRpcTraceTags.ARGS.name, JsonUtil.toJson(sofaRequest.getMethodArgs())); + } + span.tag(SofaRpcTraceTags.CLIENT_APPLICATION.name, sofaClientTraceRequest.appName()); + span.tag(SofaRpcTraceTags.SERVICE_UNIQUE_ID.name, sofaClientTraceRequest.uniqueId()); + span.tag(SofaRpcTraceTags.SERVICE.name, sofaClientTraceRequest.service()); + span.tag(SofaRpcTraceTags.METHOD.name, sofaClientTraceRequest.method()); + + context.put(TRACE_IS_ASYNC, SofaRpcCtxUtils.isAsync(sofaRequest.getInvokeType())); + context.put(CLIENT_REQUEST_CONTEXT_KEY, requestContext); + } + + /** + * Start server span + * @param context interceptor context + * @param providerInvoker sofa rpc provider invoker + * @param sofaRequest sofa request + */ + public static void startServerSpan(Context context, ProviderInvoker providerInvoker, SofaRequest sofaRequest) { + SofaServerTraceRequest sofaServerTraceRequest = new SofaServerTraceRequest(providerInvoker, sofaRequest); + RequestContext requestContext = context.serverReceive(sofaServerTraceRequest); + + Span span = requestContext.span().start(); + span.kind(sofaServerTraceRequest.kind()); + span.name(sofaServerTraceRequest.name()); + span.remoteServiceName(ConfigConst.Namespace.SOFARPC); + span.tag(SofaRpcTraceTags.SERVER_APPLICATION.name, sofaServerTraceRequest.appName()); + span.remoteIpAndPort(sofaServerTraceRequest.remoteHost(), sofaServerTraceRequest.remotePort()); + context.put(SERVER_REQUEST_CONTEXT_KEY, requestContext); + } + + /** + * Finish server span + * @param context interceptor context + * @param sofaResponse sofa response + * @param throwable rpc server exception + */ + public static void finishServerSpan(Context context, SofaResponse sofaResponse, Throwable throwable) { + finishSpan(SERVER_REQUEST_CONTEXT_KEY, context, sofaResponse, throwable); + } + + /** + * Sync finish client span + * @param context interceptor context + * @param sofaResponse sofa response + * @param throwable rpc client call exception + */ + public static void finishClientSpan(Context context, SofaResponse sofaResponse, Throwable throwable) { + if (throwable != null) { + SofaRpcCtxUtils.finishSpan(CLIENT_REQUEST_CONTEXT_KEY, context, null, throwable); + return; + } + + boolean isAsync = context.remove(TRACE_IS_ASYNC); + if (isAsync) { + return; + } + + SofaRpcCtxUtils.finishSpan(CLIENT_REQUEST_CONTEXT_KEY, context, sofaResponse, null); + } + + /** + * Async finish client span + * @param asyncContext interceptor async context + * @param result rpc call return value + */ + public static void asyncFinishClientSpan(AsyncContext asyncContext, Object result) { + try (Cleaner ignored = asyncContext.importToCurrent()) { + Context context = EaseAgent.getContext(); + RequestContext requestContext = context.remove(SofaRpcCtxUtils.CLIENT_REQUEST_CONTEXT_KEY); + + try (Scope ignoredScope = requestContext.scope()) { + Span span = requestContext.span(); + if (result instanceof Throwable) { + span.error((Throwable) result); + } else if (SofaRpcTraceBaseInterceptor.SOFA_RPC_TRACE_CONFIG.resultCollectEnabled()) { + span.tag(SofaRpcTraceTags.RESULT.name, JsonUtil.toJson(result)); + } + span.finish(); + } + } + } + + /** + * Finish span + * @param requestContextKey get the key of request context + * @param context interceptor context + * @param sofaResponse sofa rpc response + * @param throwable rpc call exception + */ + private static void finishSpan(String requestContextKey, Context context, SofaResponse sofaResponse, Throwable throwable) { + RequestContext requestContext = context.remove(requestContextKey); + try (Scope ignored = requestContext.scope()) { + Span span = requestContext.span(); + try { + if (!CLIENT_REQUEST_CONTEXT_KEY.equals(requestContextKey)) { + return; + } + if (throwable != null) { + span.error(throwable); + } else if (sofaResponse != null) { + if (sofaResponse.isError() || sofaResponse.getAppResponse() instanceof Throwable) { + span.error((Throwable) sofaResponse.getAppResponse()); + } else if (SofaRpcTraceBaseInterceptor.SOFA_RPC_TRACE_CONFIG.resultCollectEnabled()) { + span.tag(SofaRpcTraceTags.RESULT.name, JsonUtil.toJson(sofaResponse.getAppResponse())); + } + } + } finally { + span.finish(); + } + } + } + + //-------------------Sofa Rpc metrics operate method------------ + + /** + * Start collect metrics + * @param context interceptor context + * @param sofaRequest sofa rpc request + */ + public static void startCollectMetrics(Context context, SofaRequest sofaRequest) { + String methodSignature = methodSignature(sofaRequest); + context.put(BEGIN_TIME, SystemClock.now()); + context.put(METRICS_IS_ASYNC, isAsync(sofaRequest.getInvokeType())); + context.put(METRICS_KEY_NAME, methodSignature); + } + + /** + * Finish collect metrics + * @param context interceptor context + * @param sofaResponse sofa rpc response + * @param throwable call exception + */ + public static void finishCollectMetrics(Context context, SofaResponse sofaResponse, Throwable throwable) { + if (throwable != null) { + collectMetrics(context, sofaResponse, throwable); + return; + } + + boolean isAsync = context.remove(METRICS_IS_ASYNC); + if (isAsync) { + return; + } + + collectMetrics(context, sofaResponse, null); + } + + private static void collectMetrics(Context context, SofaResponse sofaResponse, Throwable throwable) { + long duration = ContextUtils.getDuration(context, BEGIN_TIME); + String methodSignature = context.remove(METRICS_KEY_NAME); + if (methodSignature == null) { + LOG.error("method signature is null"); + return; + } + boolean callResult = sofaResponse != null + && !sofaResponse.isError() + && !(sofaResponse.getAppResponse() instanceof Throwable) + && throwable == null; + SofaRpcMetricsBaseInterceptor.SOFARPC_METRICS.collect(methodSignature, duration, callResult); + } + + /** + * Async finish collect metrics + * @param asyncContext interceptor async context + * @param result sofa rpc call return value + */ + public static void asyncFinishCollectMetrics(AsyncContext asyncContext, Object result) { + try (Cleaner ignored = asyncContext.importToCurrent()) { + String methodSignature = EaseAgent.getContext().remove(METRICS_KEY_NAME); + if (methodSignature == null) { + LOG.error("method signature is null"); + return; + } + + Long duration = ContextUtils.getDuration(EaseAgent.getContext(), BEGIN_TIME); + boolean callResult = result != null && !(result instanceof Throwable); + SofaRpcMetricsBaseInterceptor.SOFARPC_METRICS.collect(methodSignature, duration, callResult); + } + } + + /** + * Check if the invoke type is async call + * @param invokeType sofa rpc invoke type + * @return returns true if the invoke type is future or callback, otherwise it returns false. + */ + private static boolean isAsync(String invokeType) { + return RpcConstants.INVOKER_TYPE_CALLBACK.equals(invokeType) || RpcConstants.INVOKER_TYPE_FUTURE.equals(invokeType); + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/SofaRpcMetricsTags.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/SofaRpcMetricsTags.java new file mode 100644 index 00000000..d0c59324 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/SofaRpcMetricsTags.java @@ -0,0 +1,15 @@ +package com.megaease.easeagent.plugin.sofarpc; + +public enum SofaRpcMetricsTags { + + CATEGORY("application"), + TYPE("sofarpc"), + LABEL_NAME("interface"), + ; + + public final String name; + + SofaRpcMetricsTags(String name) { + this.name = name; + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/SofaRpcPlugin.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/SofaRpcPlugin.java new file mode 100644 index 00000000..d65d7a87 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/SofaRpcPlugin.java @@ -0,0 +1,16 @@ +package com.megaease.easeagent.plugin.sofarpc; + +import com.megaease.easeagent.plugin.AgentPlugin; +import com.megaease.easeagent.plugin.api.config.ConfigConst; + +public class SofaRpcPlugin implements AgentPlugin { + @Override + public String getNamespace() { + return ConfigConst.Namespace.SOFARPC; + } + + @Override + public String getDomain() { + return ConfigConst.OBSERVABILITY; + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/SofaRpcTraceTags.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/SofaRpcTraceTags.java new file mode 100644 index 00000000..bbd6fc3c --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/SofaRpcTraceTags.java @@ -0,0 +1,19 @@ +package com.megaease.easeagent.plugin.sofarpc; + +public enum SofaRpcTraceTags { + SERVICE("sofa.rpc.service"), + METHOD("sofa.rpc.method"), + SERVICE_UNIQUE_ID("sofa.rpc.service.uniqueId"), + SERVER_APPLICATION("sofa.rpc.server.application"), + CLIENT_APPLICATION("sofa.rpc.client.application"), + GROUP("sofa.rpc.group"), + ARGS("sofa.rpc.args"), + RESULT("sofa.rpc.result"), + ; + + public final String name; + + SofaRpcTraceTags(String name) { + this.name = name; + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/BoltFutureInvokeCallbackConstructAdvice.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/BoltFutureInvokeCallbackConstructAdvice.java new file mode 100644 index 00000000..04308ff8 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/BoltFutureInvokeCallbackConstructAdvice.java @@ -0,0 +1,35 @@ +package com.megaease.easeagent.plugin.sofarpc.adivce; + +import com.megaease.easeagent.plugin.Points; +import com.megaease.easeagent.plugin.matcher.ClassMatcher; +import com.megaease.easeagent.plugin.matcher.IClassMatcher; +import com.megaease.easeagent.plugin.matcher.IMethodMatcher; +import com.megaease.easeagent.plugin.matcher.MethodMatcher; + +import java.util.Set; + +public class BoltFutureInvokeCallbackConstructAdvice implements Points { + private static final String BOLT_FUTURE_INVOKE_CALLBACK_FULL_CLASS_NAME = "com.alipay.sofa.rpc.message.bolt.BoltFutureInvokeCallback"; + private static final String CONSTRUCT_METHOD_NAME = ""; + private static final String BOLT_RESPONSE_FUTURE_FULL_CLASS_NAME = "com.alipay.sofa.rpc.message.bolt.BoltResponseFuture"; + + @Override + public IClassMatcher getClassMatcher() { + return ClassMatcher.builder() + .isPublic() + .hasClassName(BOLT_FUTURE_INVOKE_CALLBACK_FULL_CLASS_NAME) + .build(); + } + + @Override + public Set getMethodMatcher() { + return MethodMatcher.builder() + .isPublic() + .named(CONSTRUCT_METHOD_NAME) + .argsLength(6) + .arg(2, BOLT_RESPONSE_FUTURE_FULL_CLASS_NAME) + .build() + .toSet(); + } + +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/ConsumerAdvice.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/ConsumerAdvice.java new file mode 100644 index 00000000..cb505f56 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/ConsumerAdvice.java @@ -0,0 +1,31 @@ +package com.megaease.easeagent.plugin.sofarpc.adivce; + +import com.megaease.easeagent.plugin.Points; +import com.megaease.easeagent.plugin.matcher.*; + +import java.util.Set; + +public class ConsumerAdvice implements Points { + private static final String CONSUMER_INVOKER_CLASS_FULL_NAME = "com.alipay.sofa.rpc.filter.ConsumerInvoker"; + private static final String CONSUMER_INVOKER_METHOD_NAME = "invoke"; + private static final String CONSUMER_INVOKER_METHOD_PARAMETER_FULL_CLASS_NAME = "com.alipay.sofa.rpc.core.request.SofaRequest"; + private static final String CONSUMER_INVOKER_METHOD_RETURN_VALUE_FULL_CLASS_NAME = "com.alipay.sofa.rpc.core.response.SofaResponse"; + + @Override + public IClassMatcher getClassMatcher() { + return ClassMatcher.builder() + .hasClassName(CONSUMER_INVOKER_CLASS_FULL_NAME) + .build(); + } + + @Override + public Set getMethodMatcher() { + return MethodMatcher.builder() + .isPublic() + .named(CONSUMER_INVOKER_METHOD_NAME) + .arg(0,CONSUMER_INVOKER_METHOD_PARAMETER_FULL_CLASS_NAME) + .returnType(CONSUMER_INVOKER_METHOD_RETURN_VALUE_FULL_CLASS_NAME) + .build() + .toSet(); + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/FutureInvokeCallbackConstructAdvice.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/FutureInvokeCallbackConstructAdvice.java new file mode 100644 index 00000000..731ebdc8 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/FutureInvokeCallbackConstructAdvice.java @@ -0,0 +1,35 @@ +package com.megaease.easeagent.plugin.sofarpc.adivce; + +import com.megaease.easeagent.plugin.Points; +import com.megaease.easeagent.plugin.matcher.ClassMatcher; +import com.megaease.easeagent.plugin.matcher.IClassMatcher; +import com.megaease.easeagent.plugin.matcher.IMethodMatcher; +import com.megaease.easeagent.plugin.matcher.MethodMatcher; + +import java.util.Set; + +public class FutureInvokeCallbackConstructAdvice implements Points { + private static final String BOLT_FUTURE_INVOKE_CALLBACK_FULL_CLASS_NAME = "com.alipay.sofa.rpc.message.BoltFutureInvokeCallback"; + private static final String CONSTRUCT_METHOD_NAME = ""; + private static final String BOLT_RESPONSE_FUTURE_FULL_CLASS_NAME = "com.alipay.sofa.rpc.message.BoltResponseFuture"; + + @Override + public IClassMatcher getClassMatcher() { + return ClassMatcher.builder() + .isPublic() + .hasClassName(BOLT_FUTURE_INVOKE_CALLBACK_FULL_CLASS_NAME) + .build(); + } + + @Override + public Set getMethodMatcher() { + return MethodMatcher.builder() + .isPublic() + .named(CONSTRUCT_METHOD_NAME) + .argsLength(6) + .arg(2, BOLT_RESPONSE_FUTURE_FULL_CLASS_NAME) + .build() + .toSet(); + } + +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/ProviderAdvice.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/ProviderAdvice.java new file mode 100644 index 00000000..ad3d492a --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/ProviderAdvice.java @@ -0,0 +1,34 @@ +package com.megaease.easeagent.plugin.sofarpc.adivce; + +import com.megaease.easeagent.plugin.Points; +import com.megaease.easeagent.plugin.matcher.ClassMatcher; +import com.megaease.easeagent.plugin.matcher.IClassMatcher; +import com.megaease.easeagent.plugin.matcher.IMethodMatcher; +import com.megaease.easeagent.plugin.matcher.MethodMatcher; + +import java.util.Set; + +public class ProviderAdvice implements Points { + private static final String PROVIDER_INVOKER_CLASS_FULL_NAME = "com.alipay.sofa.rpc.filter.ProviderInvoker"; + private static final String PROVIDER_INVOKER_METHOD_NAME = "invoke"; + private static final String PROVIDER_INVOKER_METHOD_PARAMETER_FULL_CLASS_NAME = "com.alipay.sofa.rpc.core.request.SofaRequest"; + private static final String PROVIDER_INVOKER_METHOD_RETURN_VALUE_FULL_CLASS_NAME = "com.alipay.sofa.rpc.core.response.SofaResponse"; + + @Override + public IClassMatcher getClassMatcher() { + return ClassMatcher.builder() + .hasClassName(PROVIDER_INVOKER_CLASS_FULL_NAME) + .build(); + } + + @Override + public Set getMethodMatcher() { + return MethodMatcher.builder() + .isPublic() + .named(PROVIDER_INVOKER_METHOD_NAME) + .arg(0,PROVIDER_INVOKER_METHOD_PARAMETER_FULL_CLASS_NAME) + .returnType(PROVIDER_INVOKER_METHOD_RETURN_VALUE_FULL_CLASS_NAME) + .build() + .toSet(); + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/ResponseCallbackAdvice.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/ResponseCallbackAdvice.java new file mode 100644 index 00000000..220e36b7 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/ResponseCallbackAdvice.java @@ -0,0 +1,39 @@ +package com.megaease.easeagent.plugin.sofarpc.adivce; + +import com.megaease.easeagent.plugin.Points; +import com.megaease.easeagent.plugin.matcher.ClassMatcher; +import com.megaease.easeagent.plugin.matcher.IClassMatcher; +import com.megaease.easeagent.plugin.matcher.IMethodMatcher; +import com.megaease.easeagent.plugin.matcher.MethodMatcher; + +import java.util.Set; + +public class ResponseCallbackAdvice implements Points { + private static final String BOLT_INVOKER_CALLBACK_FULL_CLASS_NAME = "com.alipay.sofa.rpc.message.BoltInvokerCallback"; + private static final String GREATER_THAN_VERSION_5_3_0_BOLT_INVOKER_CALLBACK_FULL_CLASS_NAME = "com.alipay.sofa.rpc.message.bolt.BoltInvokerCallback"; + private static final String CONSTRUCT_METHOD_NAME = ""; + private static final String SOFA_RESPONSE_CALLBACK_FULL_CLASS_NAME = "com.alipay.sofa.rpc.core.invoke.SofaResponseCallback"; + + + @Override + public IClassMatcher getClassMatcher() { + return ClassMatcher.builder() + .isPublic() + .hasClassName(BOLT_INVOKER_CALLBACK_FULL_CLASS_NAME) + .or() + .isPublic() + .hasClassName(GREATER_THAN_VERSION_5_3_0_BOLT_INVOKER_CALLBACK_FULL_CLASS_NAME) + .build(); + } + + @Override + public Set getMethodMatcher() { + return MethodMatcher.builder() + .isPublic() + .named(CONSTRUCT_METHOD_NAME) + .argsLength(6) + .arg(2, SOFA_RESPONSE_CALLBACK_FULL_CLASS_NAME) + .build() + .toSet(); + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/ResponseFutureAdvice.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/ResponseFutureAdvice.java new file mode 100644 index 00000000..e33fb214 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/adivce/ResponseFutureAdvice.java @@ -0,0 +1,47 @@ +package com.megaease.easeagent.plugin.sofarpc.adivce; + +import com.megaease.easeagent.plugin.Points; +import com.megaease.easeagent.plugin.matcher.ClassMatcher; +import com.megaease.easeagent.plugin.matcher.IClassMatcher; +import com.megaease.easeagent.plugin.matcher.IMethodMatcher; +import com.megaease.easeagent.plugin.matcher.MethodMatcher; + +import java.util.Set; + +public class ResponseFutureAdvice implements Points { + private static final String ABSTRACT_RESPONSE_FUTURE_FULL_CLASS_NAME = "com.alipay.sofa.rpc.message.AbstractResponseFuture"; + private static final String BOLT_RESPONSE_FUTURE_FULL_CLASS_NAME = "com.alipay.sofa.rpc.message.BoltResponseFuture"; + private static final String SET_SUCCESS_METHOD_NAME = "setSuccess"; + private static final String SET_FAILURE_METHOD_NAME = "setFailure"; + + @Override + public IClassMatcher getClassMatcher() { + return ClassMatcher.builder() + .isPublic() + .isAbstract() + .hasClassName(ABSTRACT_RESPONSE_FUTURE_FULL_CLASS_NAME) + .or() + .isPublic() + .hasClassName(BOLT_RESPONSE_FUTURE_FULL_CLASS_NAME) + .build(); + } + + @Override + public Set getMethodMatcher() { + return MethodMatcher.builder() + .named(SET_SUCCESS_METHOD_NAME) + .argsLength(1) + .arg(0,Object.class.getName()) + .or() + .named(SET_FAILURE_METHOD_NAME) + .argsLength(1) + .arg(0, Throwable.class.getName()) + .build() + .toSet(); + } + + @Override + public boolean isAddDynamicField() { + return true; + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/config/SofaRpcTraceConfig.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/config/SofaRpcTraceConfig.java new file mode 100644 index 00000000..2e9ec759 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/config/SofaRpcTraceConfig.java @@ -0,0 +1,38 @@ +package com.megaease.easeagent.plugin.sofarpc.config; + +import com.megaease.easeagent.plugin.api.config.AutoRefreshConfigSupplier; +import com.megaease.easeagent.plugin.api.config.AutoRefreshPluginConfig; +import com.megaease.easeagent.plugin.api.config.IPluginConfig; +import com.megaease.easeagent.plugin.utils.Pair; + +public class SofaRpcTraceConfig implements AutoRefreshPluginConfig { + + private final Pair argsCollectEnabledPair = new Pair<>("args.collect.enabled", false); + private final Pair resultCollectEnabledPair = new Pair<>("result.collect.enabled", false); + private volatile boolean argsCollectEnabled = argsCollectEnabledPair.getValue(); + private volatile boolean resultCollectEnabled = resultCollectEnabledPair.getValue(); + + public boolean argsCollectEnabled() { + return argsCollectEnabled; + } + + public boolean resultCollectEnabled() { + return resultCollectEnabled; + } + + public static final AutoRefreshConfigSupplier SUPPLIER = new AutoRefreshConfigSupplier() { + @Override + public SofaRpcTraceConfig newInstance() { + return new SofaRpcTraceConfig(); + } + }; + + @Override + public void onChange(IPluginConfig oldConfig, IPluginConfig newConfig) { + String argsCollectEnabled = newConfig.getString(argsCollectEnabledPair.getKey()); + this.argsCollectEnabled = Boolean.parseBoolean(argsCollectEnabled); + + String resultCollectEnabled = newConfig.getString(resultCollectEnabledPair.getKey()); + this.resultCollectEnabled = Boolean.parseBoolean(resultCollectEnabled); + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/initalize/SofaRpcFutureInvokeCallbackConstructInterceptor.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/initalize/SofaRpcFutureInvokeCallbackConstructInterceptor.java new file mode 100644 index 00000000..9b2bc65f --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/initalize/SofaRpcFutureInvokeCallbackConstructInterceptor.java @@ -0,0 +1,31 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.initalize; + +import com.megaease.easeagent.plugin.annotation.AdviceTo; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.api.context.RequestContext; +import com.megaease.easeagent.plugin.api.trace.Scope; +import com.megaease.easeagent.plugin.field.AgentDynamicFieldAccessor; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcPlugin; +import com.megaease.easeagent.plugin.sofarpc.adivce.FutureInvokeCallbackConstructAdvice; +import com.megaease.easeagent.plugin.sofarpc.adivce.BoltFutureInvokeCallbackConstructAdvice; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.SofaRpcTraceBaseInterceptor; + +@AdviceTo(value = FutureInvokeCallbackConstructAdvice.class, plugin = SofaRpcPlugin.class) +@AdviceTo(value = BoltFutureInvokeCallbackConstructAdvice.class, plugin = SofaRpcPlugin.class) +public class SofaRpcFutureInvokeCallbackConstructInterceptor extends SofaRpcTraceBaseInterceptor { + + @Override + public void before(MethodInfo methodInfo, Context context) { + RequestContext requestContext = context.get(SofaRpcCtxUtils.CLIENT_REQUEST_CONTEXT_KEY); + String methodSignature = context.get(SofaRpcCtxUtils.METRICS_KEY_NAME); + if (requestContext != null) { + try (Scope ignore = requestContext.scope()) { + AgentDynamicFieldAccessor.setDynamicFieldValue(methodInfo.getArgs()[2], context.exportAsync()); + } + } else if (methodSignature != null) { + AgentDynamicFieldAccessor.setDynamicFieldValue(methodInfo.getArgs()[2], context.exportAsync()); + } + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/SofaRpcMetrics.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/SofaRpcMetrics.java new file mode 100644 index 00000000..49b5efba --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/SofaRpcMetrics.java @@ -0,0 +1,99 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.metrics; + +import com.megaease.easeagent.plugin.api.metric.*; +import com.megaease.easeagent.plugin.api.metric.name.MetricField; +import com.megaease.easeagent.plugin.api.metric.name.MetricSubType; +import com.megaease.easeagent.plugin.api.metric.name.MetricValueFetcher; +import com.megaease.easeagent.plugin.api.metric.name.NameFactory; +import com.megaease.easeagent.plugin.tools.metrics.LastMinutesCounterGauge; +import com.megaease.easeagent.plugin.utils.ImmutableMap; + +import java.util.concurrent.TimeUnit; + +public class SofaRpcMetrics extends ServiceMetric { + + public static final ServiceMetricSupplier SOFARPC_METRICS_SUPPLIER = new ServiceMetricSupplier() { + @Override + public NameFactory newNameFactory() { + return nameFactory(); + } + + @Override + public SofaRpcMetrics newInstance(MetricRegistry metricRegistry, NameFactory nameFactory) { + return new SofaRpcMetrics(metricRegistry, nameFactory); + } + }; + + public SofaRpcMetrics(MetricRegistry metricRegistry, NameFactory nameFactory) { + super(metricRegistry, nameFactory); + } + + private static NameFactory nameFactory() { + return NameFactory.createBuilder() + .timerType(MetricSubType.DEFAULT, + ImmutableMap.builder() + .put(MetricField.MIN_EXECUTION_TIME, MetricValueFetcher.SnapshotMinValue) + .put(MetricField.MAX_EXECUTION_TIME, MetricValueFetcher.SnapshotMaxValue) + .put(MetricField.MEAN_EXECUTION_TIME, MetricValueFetcher.SnapshotMeanValue) + .put(MetricField.P25_EXECUTION_TIME, MetricValueFetcher.Snapshot25Percentile) + .put(MetricField.P50_EXECUTION_TIME, MetricValueFetcher.Snapshot50PercentileValue) + .put(MetricField.P75_EXECUTION_TIME, MetricValueFetcher.Snapshot75PercentileValue) + .put(MetricField.P95_EXECUTION_TIME, MetricValueFetcher.Snapshot95PercentileValue) + .put(MetricField.P98_EXECUTION_TIME, MetricValueFetcher.Snapshot98PercentileValue) + .put(MetricField.P99_EXECUTION_TIME, MetricValueFetcher.Snapshot99PercentileValue) + .put(MetricField.P999_EXECUTION_TIME, MetricValueFetcher.Snapshot999PercentileValue) + .build()) + .meterType(MetricSubType.DEFAULT, + ImmutableMap.builder() + .put(MetricField.M1_RATE, MetricValueFetcher.MeteredM1Rate) + .put(MetricField.M5_RATE, MetricValueFetcher.MeteredM5Rate) + .put(MetricField.M15_RATE, MetricValueFetcher.MeteredM15Rate) + .put(MetricField.MEAN_RATE, MetricValueFetcher.MeteredMeanRate) + .build()) + .meterType(MetricSubType.ERROR, + ImmutableMap.builder() + .put(MetricField.M1_ERROR_RATE, MetricValueFetcher.MeteredM1Rate) + .put(MetricField.M5_ERROR_RATE, MetricValueFetcher.MeteredM5Rate) + .put(MetricField.M15_ERROR_RATE, MetricValueFetcher.MeteredM15Rate) + .build()) + .counterType(MetricSubType.DEFAULT, + ImmutableMap.builder() + .put(MetricField.EXECUTION_COUNT, MetricValueFetcher.CountingCount) + .build()) + .counterType(MetricSubType.ERROR, + ImmutableMap.builder() + .put(MetricField.EXECUTION_ERROR_COUNT, MetricValueFetcher.CountingCount) + .build()) + .gaugeType(MetricSubType.DEFAULT, + ImmutableMap.builder() + .build()) + .build(); + } + + public void collect(String key, long duration, boolean success) { + + this.timer(key, MetricSubType.DEFAULT).update(duration, TimeUnit.MILLISECONDS); + final Meter meter = this.meter(key, MetricSubType.DEFAULT); + final Counter counter = this.counter(key, MetricSubType.DEFAULT); + meter.mark(); + counter.inc(); + + if (!success) { + final Meter errorMeter = this.meter(key, MetricSubType.ERROR); + final Counter errorCounter = this.counter(key, MetricSubType.ERROR); + errorMeter.mark(); + errorCounter.inc(); + } + + this.gauge(key, MetricSubType.DEFAULT, new MetricSupplier() { + @Override + public Gauge newMetric() { + return () -> LastMinutesCounterGauge.builder() + .m1Count((long) (meter.getOneMinuteRate() * 60)) + .m5Count((long) (meter.getFiveMinuteRate() * 60 * 5)) + .m15Count((long) (meter.getFifteenMinuteRate() * 60 * 15)) + .build(); + } + }); + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/SofaRpcMetricsBaseInterceptor.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/SofaRpcMetricsBaseInterceptor.java new file mode 100644 index 00000000..a4e97377 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/SofaRpcMetricsBaseInterceptor.java @@ -0,0 +1,28 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.metrics; + +import com.megaease.easeagent.plugin.api.config.IPluginConfig; +import com.megaease.easeagent.plugin.api.metric.ServiceMetricRegistry; +import com.megaease.easeagent.plugin.api.metric.name.Tags; +import com.megaease.easeagent.plugin.enums.Order; +import com.megaease.easeagent.plugin.interceptor.NonReentrantInterceptor; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcMetricsTags; + +public abstract class SofaRpcMetricsBaseInterceptor implements NonReentrantInterceptor { + public static volatile SofaRpcMetrics SOFARPC_METRICS; + + @Override + public String getType() { + return Order.METRIC.getName(); + } + + @Override + public int order() { + return Order.METRIC.getOrder(); + } + + @Override + public void init(IPluginConfig config, String className, String methodName, String methodDescriptor) { + Tags tags = new Tags(SofaRpcMetricsTags.CATEGORY.name, SofaRpcMetricsTags.TYPE.name, SofaRpcMetricsTags.LABEL_NAME.name); + SOFARPC_METRICS = ServiceMetricRegistry.getOrCreate(config, tags, SofaRpcMetrics.SOFARPC_METRICS_SUPPLIER); + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/callback/SofaRpcResponseCallbackMetrics.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/callback/SofaRpcResponseCallbackMetrics.java new file mode 100644 index 00000000..fbc2660c --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/callback/SofaRpcResponseCallbackMetrics.java @@ -0,0 +1,44 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.callback; + +import com.alipay.sofa.rpc.core.exception.SofaRpcException; +import com.alipay.sofa.rpc.core.invoke.SofaResponseCallback; +import com.alipay.sofa.rpc.core.request.RequestBase; +import com.megaease.easeagent.plugin.api.context.AsyncContext; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; + +public class SofaRpcResponseCallbackMetrics implements SofaResponseCallback { + private final SofaResponseCallback sofaResponseCallback; + private final AsyncContext asyncContext; + + public SofaRpcResponseCallbackMetrics(SofaResponseCallback sofaResponseCallback, AsyncContext asyncContext) { + this.sofaResponseCallback = sofaResponseCallback; + this.asyncContext = asyncContext; + } + + @Override + public void onAppResponse(Object appResponse, String methodName, RequestBase request) { + try { + this.sofaResponseCallback.onAppResponse(appResponse, methodName, request); + } finally { + SofaRpcCtxUtils.asyncFinishCollectMetrics(this.asyncContext, appResponse); + } + } + + @Override + public void onAppException(Throwable throwable, String methodName, RequestBase request) { + try { + this.sofaResponseCallback.onAppException(throwable, methodName, request); + } finally { + SofaRpcCtxUtils.asyncFinishCollectMetrics(this.asyncContext, throwable); + } + } + + @Override + public void onSofaException(SofaRpcException sofaException, String methodName, RequestBase request) { + try { + this.sofaResponseCallback.onSofaException(sofaException, methodName, request); + } finally { + SofaRpcCtxUtils.asyncFinishCollectMetrics(this.asyncContext, sofaException); + } + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/callback/SofaRpcResponseCallbackMetricsInterceptor.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/callback/SofaRpcResponseCallbackMetricsInterceptor.java new file mode 100644 index 00000000..5df4ff1a --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/callback/SofaRpcResponseCallbackMetricsInterceptor.java @@ -0,0 +1,20 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.callback; + +import com.alipay.sofa.rpc.core.invoke.SofaResponseCallback; +import com.megaease.easeagent.plugin.annotation.AdviceTo; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcPlugin; +import com.megaease.easeagent.plugin.sofarpc.adivce.ResponseCallbackAdvice; +import com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.SofaRpcMetricsBaseInterceptor; + +@AdviceTo(value = ResponseCallbackAdvice.class, plugin = SofaRpcPlugin.class) +public class SofaRpcResponseCallbackMetricsInterceptor extends SofaRpcMetricsBaseInterceptor { + + @Override + public void before(MethodInfo methodInfo, Context context) { + SofaResponseCallback sofaResponseCallback = (SofaResponseCallback) methodInfo.getArgs()[2]; + methodInfo.changeArg(2, new SofaRpcResponseCallbackMetrics(sofaResponseCallback, context.exportAsync())); + } + +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/common/SofaRpcMetricsInterceptor.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/common/SofaRpcMetricsInterceptor.java new file mode 100644 index 00000000..d4876c97 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/common/SofaRpcMetricsInterceptor.java @@ -0,0 +1,28 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.common; + +import com.alipay.sofa.rpc.core.request.SofaRequest; +import com.alipay.sofa.rpc.core.response.SofaResponse; +import com.megaease.easeagent.plugin.annotation.AdviceTo; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcPlugin; +import com.megaease.easeagent.plugin.sofarpc.adivce.ConsumerAdvice; +import com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.SofaRpcMetricsBaseInterceptor; + +@AdviceTo(value = ConsumerAdvice.class, plugin = SofaRpcPlugin.class) +public class SofaRpcMetricsInterceptor extends SofaRpcMetricsBaseInterceptor { + + @Override + public void before(MethodInfo methodInfo, Context context) { + SofaRequest sofaRequest = (SofaRequest) methodInfo.getArgs()[0]; + SofaRpcCtxUtils.startCollectMetrics(context, sofaRequest); + } + + + @Override + public void after(MethodInfo methodInfo, Context context) { + SofaResponse retValue = (SofaResponse) methodInfo.getRetValue(); + SofaRpcCtxUtils.finishCollectMetrics(context, retValue, methodInfo.getThrowable()); + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/future/SofaRpcResponseFutureMetricsInterceptor.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/future/SofaRpcResponseFutureMetricsInterceptor.java new file mode 100644 index 00000000..ecc75b67 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/future/SofaRpcResponseFutureMetricsInterceptor.java @@ -0,0 +1,22 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.future; + +import com.megaease.easeagent.plugin.annotation.AdviceTo; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.api.context.AsyncContext; +import com.megaease.easeagent.plugin.field.AgentDynamicFieldAccessor; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcPlugin; +import com.megaease.easeagent.plugin.sofarpc.adivce.ResponseFutureAdvice; +import com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.SofaRpcMetricsBaseInterceptor; + +@AdviceTo(value = ResponseFutureAdvice.class, plugin = SofaRpcPlugin.class) +public class SofaRpcResponseFutureMetricsInterceptor extends SofaRpcMetricsBaseInterceptor { + + @Override + public void after(MethodInfo methodInfo, Context context) { + Object result = methodInfo.getArgs()[0]; + AsyncContext asyncContext = AgentDynamicFieldAccessor.getDynamicFieldValue(methodInfo.getInvoker()); + SofaRpcCtxUtils.asyncFinishCollectMetrics(asyncContext, result); + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/SofaRpcTraceBaseInterceptor.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/SofaRpcTraceBaseInterceptor.java new file mode 100644 index 00000000..26cc3e4c --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/SofaRpcTraceBaseInterceptor.java @@ -0,0 +1,28 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.trace; + +import com.megaease.easeagent.plugin.api.config.AutoRefreshPluginConfigRegistry; +import com.megaease.easeagent.plugin.api.config.ConfigConst; +import com.megaease.easeagent.plugin.api.config.IPluginConfig; +import com.megaease.easeagent.plugin.enums.Order; +import com.megaease.easeagent.plugin.interceptor.NonReentrantInterceptor; +import com.megaease.easeagent.plugin.sofarpc.config.SofaRpcTraceConfig; + +public abstract class SofaRpcTraceBaseInterceptor implements NonReentrantInterceptor { + public static volatile SofaRpcTraceConfig SOFA_RPC_TRACE_CONFIG; + + @Override + public String getType() { + return Order.TRACING.getName(); + } + + @Override + public int order() { + return Order.TRACING.getOrder(); + } + + @Override + public void init(IPluginConfig config, String className, String methodName, String methodDescriptor) { + SOFA_RPC_TRACE_CONFIG = AutoRefreshPluginConfigRegistry.getOrCreate(ConfigConst.OBSERVABILITY, ConfigConst.Namespace.SOFARPC, this.getType(), SofaRpcTraceConfig.SUPPLIER); + } + +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/callback/SofaRpcResponseCallbackTrace.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/callback/SofaRpcResponseCallbackTrace.java new file mode 100644 index 00000000..a3bc1d59 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/callback/SofaRpcResponseCallbackTrace.java @@ -0,0 +1,44 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.trace.callback; + +import com.alipay.sofa.rpc.core.exception.SofaRpcException; +import com.alipay.sofa.rpc.core.invoke.SofaResponseCallback; +import com.alipay.sofa.rpc.core.request.RequestBase; +import com.megaease.easeagent.plugin.api.context.AsyncContext; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; + +public class SofaRpcResponseCallbackTrace implements SofaResponseCallback { + private final SofaResponseCallback sofaResponseCallback; + private final AsyncContext asyncContext; + + public SofaRpcResponseCallbackTrace(SofaResponseCallback sofaResponseCallback, AsyncContext asyncContext) { + this.sofaResponseCallback = sofaResponseCallback; + this.asyncContext = asyncContext; + } + + @Override + public void onAppResponse(Object appResponse, String methodName, RequestBase request) { + try { + this.sofaResponseCallback.onAppResponse(appResponse, methodName, request); + } finally { + SofaRpcCtxUtils.asyncFinishClientSpan(this.asyncContext, appResponse); + } + } + + @Override + public void onAppException(Throwable throwable, String methodName, RequestBase request) { + try { + this.sofaResponseCallback.onAppException(throwable, methodName, request); + } finally { + SofaRpcCtxUtils.asyncFinishClientSpan(this.asyncContext, throwable); + } + } + + @Override + public void onSofaException(SofaRpcException sofaException, String methodName, RequestBase request) { + try { + this.sofaResponseCallback.onSofaException(sofaException, methodName, request); + } finally { + SofaRpcCtxUtils.asyncFinishClientSpan(this.asyncContext, sofaException); + } + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/callback/SofaRpcResponseCallbackTraceInterceptor.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/callback/SofaRpcResponseCallbackTraceInterceptor.java new file mode 100644 index 00000000..feb466cd --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/callback/SofaRpcResponseCallbackTraceInterceptor.java @@ -0,0 +1,28 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.trace.callback; + +import com.alipay.sofa.rpc.core.invoke.SofaResponseCallback; +import com.megaease.easeagent.plugin.annotation.AdviceTo; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.api.context.AsyncContext; +import com.megaease.easeagent.plugin.api.context.RequestContext; +import com.megaease.easeagent.plugin.api.trace.Scope; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcPlugin; +import com.megaease.easeagent.plugin.sofarpc.adivce.ResponseCallbackAdvice; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.SofaRpcTraceBaseInterceptor; + +@AdviceTo(value = ResponseCallbackAdvice.class, plugin = SofaRpcPlugin.class) +public class SofaRpcResponseCallbackTraceInterceptor extends SofaRpcTraceBaseInterceptor { + + @Override + public void before(MethodInfo methodInfo, Context context) { + SofaResponseCallback sofaResponseCallback = (SofaResponseCallback) methodInfo.getArgs()[2]; + + RequestContext requestContext = context.get(SofaRpcCtxUtils.CLIENT_REQUEST_CONTEXT_KEY); + try (Scope ignore = requestContext.scope()) { + AsyncContext asyncContext = context.exportAsync(); + methodInfo.changeArg(2, new SofaRpcResponseCallbackTrace(sofaResponseCallback, asyncContext)); + } + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaClientTraceRequest.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaClientTraceRequest.java new file mode 100644 index 00000000..32089c4b --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaClientTraceRequest.java @@ -0,0 +1,68 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.trace.common; + +import com.alipay.sofa.rpc.context.RpcInternalContext; +import com.alipay.sofa.rpc.core.request.SofaRequest; +import com.alipay.sofa.rpc.filter.ConsumerInvoker; +import com.megaease.easeagent.plugin.api.trace.Request; +import com.megaease.easeagent.plugin.api.trace.Span; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; + +public class SofaClientTraceRequest implements Request { + private final ConsumerInvoker consumerInvoker; + private final SofaRequest sofaRequest; + + public SofaClientTraceRequest(ConsumerInvoker consumerInvoker, SofaRequest sofaRequest) { + this.consumerInvoker = consumerInvoker; + this.sofaRequest = sofaRequest; + } + + @Override + public Span.Kind kind() { + return Span.Kind.CLIENT; + } + + @Override + public String header(String name) { + return (String) this.sofaRequest.getRequestProp(name); + } + + @Override + public String name() { + return SofaRpcCtxUtils.name(this.sofaRequest); + } + + @Override + public boolean cacheScope() { + return false; + } + + @Override + public void setHeader(String name, String value) { + this.sofaRequest.addRequestProp(name,value); + } + + public String service() { + return this.sofaRequest.getInterfaceName(); + } + + public String method() { + return SofaRpcCtxUtils.method(this.sofaRequest); + } + + public String uniqueId() { + return consumerInvoker.getConfig().getUniqueId(); + } + + public String appName() { + return consumerInvoker.getConfig().getAppName(); + } + + + public String remoteHost() { + return RpcInternalContext.getContext().getProviderInfo().getHost(); + } + + public int remotePort() { + return RpcInternalContext.getContext().getProviderInfo().getPort(); + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaRpcConsumerTraceInterceptor.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaRpcConsumerTraceInterceptor.java new file mode 100644 index 00000000..eab59492 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaRpcConsumerTraceInterceptor.java @@ -0,0 +1,31 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.trace.common; + +import com.alipay.sofa.rpc.core.request.SofaRequest; +import com.alipay.sofa.rpc.core.response.SofaResponse; +import com.alipay.sofa.rpc.filter.ConsumerInvoker; +import com.megaease.easeagent.plugin.annotation.AdviceTo; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcPlugin; +import com.megaease.easeagent.plugin.sofarpc.adivce.ConsumerAdvice; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.SofaRpcTraceBaseInterceptor; + +@AdviceTo(value = ConsumerAdvice.class, plugin = SofaRpcPlugin.class) +public class SofaRpcConsumerTraceInterceptor extends SofaRpcTraceBaseInterceptor { + + @Override + public void before(MethodInfo methodInfo, Context context) { + Object[] args = methodInfo.getArgs(); + SofaRequest sofaRequest = (SofaRequest) args[0]; + ConsumerInvoker consumerInvoker = (ConsumerInvoker) methodInfo.getInvoker(); + SofaRpcCtxUtils.startClientSpan(context, sofaRequest, consumerInvoker); + } + + + @Override + public void after(MethodInfo methodInfo, Context context) { + SofaResponse sofaResponse = (SofaResponse) methodInfo.getRetValue(); + SofaRpcCtxUtils.finishClientSpan(context, sofaResponse, methodInfo.getThrowable()); + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaRpcProviderTraceInterceptor.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaRpcProviderTraceInterceptor.java new file mode 100644 index 00000000..f8989a48 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaRpcProviderTraceInterceptor.java @@ -0,0 +1,31 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.trace.common; + +import com.alipay.sofa.rpc.core.request.SofaRequest; +import com.alipay.sofa.rpc.core.response.SofaResponse; +import com.alipay.sofa.rpc.filter.ProviderInvoker; +import com.megaease.easeagent.plugin.annotation.AdviceTo; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcPlugin; +import com.megaease.easeagent.plugin.sofarpc.adivce.ProviderAdvice; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.SofaRpcTraceBaseInterceptor; + +@AdviceTo(value = ProviderAdvice.class, plugin = SofaRpcPlugin.class) +public class SofaRpcProviderTraceInterceptor extends SofaRpcTraceBaseInterceptor { + + @Override + public void before(MethodInfo methodInfo, Context context) { + + ProviderInvoker providerInvoker = (ProviderInvoker)methodInfo.getInvoker(); + SofaRequest sofaRequest = (SofaRequest)methodInfo.getArgs()[0]; + SofaRpcCtxUtils.startServerSpan(context, providerInvoker, sofaRequest); + } + + + @Override + public void after(MethodInfo methodInfo, Context context) { + SofaResponse sofaResponse = (SofaResponse) methodInfo.getRetValue(); + SofaRpcCtxUtils.finishServerSpan(context, sofaResponse, methodInfo.getThrowable()); + } +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaServerTraceRequest.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaServerTraceRequest.java new file mode 100644 index 00000000..f2221466 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaServerTraceRequest.java @@ -0,0 +1,61 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.trace.common; + +import com.alipay.sofa.rpc.context.RpcInternalContext; +import com.alipay.sofa.rpc.core.request.SofaRequest; +import com.alipay.sofa.rpc.filter.ProviderInvoker; +import com.megaease.easeagent.plugin.api.trace.Request; +import com.megaease.easeagent.plugin.api.trace.Span; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; + +import java.net.InetSocketAddress; + +public class SofaServerTraceRequest implements Request { + + private final ProviderInvoker providerInvoker; + private final SofaRequest sofaRequest; + + public SofaServerTraceRequest(ProviderInvoker providerInvoker, SofaRequest sofaRequest) { + this.providerInvoker = providerInvoker; + this.sofaRequest = sofaRequest; + } + + @Override + public Span.Kind kind() { + return Span.Kind.SERVER; + } + + @Override + public String header(String name) { + return (String) this.sofaRequest.getRequestProp(name); + } + + @Override + public String name() { + return SofaRpcCtxUtils.name(this.sofaRequest); + } + + @Override + public boolean cacheScope() { + return false; + } + + @Override + public void setHeader(String name, String value) { + this.sofaRequest.addRequestProp(name,value); + } + + public String appName() { + return this.providerInvoker.getConfig().getAppName(); + } + + public String remoteHost() { + InetSocketAddress remoteAddress = RpcInternalContext.getContext().getRemoteAddress(); + return remoteAddress != null ? remoteAddress.getHostString() : null; + } + + public int remotePort() { + InetSocketAddress remoteAddress = RpcInternalContext.getContext().getRemoteAddress(); + return remoteAddress != null ? remoteAddress.getPort() : 0; + } + +} diff --git a/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/future/SofaRpcResponseFutureTraceInterceptor.java b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/future/SofaRpcResponseFutureTraceInterceptor.java new file mode 100644 index 00000000..f86f6de1 --- /dev/null +++ b/plugins/sofarpc/src/main/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/future/SofaRpcResponseFutureTraceInterceptor.java @@ -0,0 +1,26 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.trace.future; + +import com.megaease.easeagent.plugin.annotation.AdviceTo; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.api.context.AsyncContext; +import com.megaease.easeagent.plugin.field.AgentDynamicFieldAccessor; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcPlugin; +import com.megaease.easeagent.plugin.sofarpc.adivce.ResponseFutureAdvice; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.SofaRpcTraceBaseInterceptor; + +@AdviceTo(value = ResponseFutureAdvice.class, plugin = SofaRpcPlugin.class) +public class SofaRpcResponseFutureTraceInterceptor extends SofaRpcTraceBaseInterceptor { + + @Override + public void after(MethodInfo methodInfo, Context context) { + Object result = methodInfo.getArgs()[0]; + AsyncContext asyncContext = AgentDynamicFieldAccessor.getDynamicFieldValue(methodInfo.getInvoker()); + + if (methodInfo.getThrowable() != null) { + result = methodInfo.getThrowable(); + } + SofaRpcCtxUtils.asyncFinishClientSpan(asyncContext, result); + } +} diff --git a/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/BaseInterceptorTest.java b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/BaseInterceptorTest.java new file mode 100644 index 00000000..2d13f1b6 --- /dev/null +++ b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/BaseInterceptorTest.java @@ -0,0 +1,150 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor; + +import com.alipay.sofa.rpc.common.RpcConstants; +import com.alipay.sofa.rpc.config.ConsumerConfig; +import com.alipay.sofa.rpc.config.ProviderConfig; +import com.alipay.sofa.rpc.context.RpcInternalContext; +import com.alipay.sofa.rpc.core.request.SofaRequest; +import com.alipay.sofa.rpc.filter.ConsumerInvoker; +import com.alipay.sofa.rpc.filter.ProviderInvoker; +import com.megaease.easeagent.mock.config.MockConfig; +import com.megaease.easeagent.mock.plugin.api.MockEaseAgent; +import com.megaease.easeagent.mock.plugin.api.utils.InterceptorTestUtils; +import com.megaease.easeagent.plugin.api.config.ConfigConst; +import com.megaease.easeagent.plugin.api.trace.Span; +import com.megaease.easeagent.plugin.bridge.EaseAgent; +import com.megaease.easeagent.plugin.enums.Order; +import com.megaease.easeagent.plugin.report.tracing.ReportSpan; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcPlugin; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcTraceTags; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.SofaRpcTraceBaseInterceptor; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.common.SofaRpcConsumerTraceInterceptor; +import com.megaease.easeagent.plugin.utils.common.JsonUtil; +import org.junit.*; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.lang.reflect.Method; + +import static org.mockito.Mockito.when; + +public abstract class BaseInterceptorTest { + + protected RpcInternalContext rpcContext = RpcInternalContext.getContext(); + + @Mock + protected SofaRequest sofaRequest; + + @Mock + protected ConsumerInvoker consumerInvoker; + + @Mock + protected ConsumerConfig consumerConfig; + + @Mock + protected ProviderInvoker providerInvoker; + + @Mock + protected ProviderConfig providerConfig; + + @Mock + protected Method mockMethod; + + protected Object[] allArguments; + + private AutoCloseable autoCloseable; + + @BeforeClass + public static void beforeClass() { + } + + @AfterClass + public static void afterClass() { + } + + @Test + public void testGetType() { + String type = getInterceptor().getType(); + Assert.assertEquals(Order.TRACING.getName(), type); + } + + @Test + public void testOrder() { + int order = getInterceptor().order(); + Assert.assertEquals(Order.TRACING.getOrder(), order); + } + + @Before + public void init() { + autoCloseable = MockitoAnnotations.openMocks(this); + when(consumerInvoker.getConfig()).thenReturn(consumerConfig); + when(consumerConfig.getAppName()).thenReturn("sofa-client"); + + when(providerInvoker.getConfig()).thenReturn(providerConfig); + when(providerConfig.getAppName()).thenReturn("sofa-server"); + + when(sofaRequest.getMethod()).thenReturn(mockMethod); + @SuppressWarnings("unchecked") + Class declaringClass = (Class) mockMethod.getDeclaringClass(); + when(declaringClass).thenReturn(BaseInterceptorTest.class); + when(mockMethod.getName()).thenReturn("mock"); + when(mockMethod.getParameterTypes()).thenReturn(new Class[]{String.class, Integer.class}); + when(sofaRequest.getInvokeType()).thenReturn(RpcConstants.INVOKER_TYPE_SYNC); + when(sofaRequest.getMethodArgs()).thenReturn(new Object[]{"abc", 3}); + + allArguments = new Object[]{sofaRequest}; + + EaseAgent.configFactory = MockConfig.getPluginConfigManager(); + SofaRpcPlugin sofaRpcPlugin = new SofaRpcPlugin(); + SofaRpcTraceBaseInterceptor sofaRpcTraceBaseInterceptor = getInterceptor(); + InterceptorTestUtils.init(sofaRpcTraceBaseInterceptor, sofaRpcPlugin); + } + + @After + public void destroy() throws Exception { + autoCloseable.close(); + } + + protected abstract SofaRpcTraceBaseInterceptor getInterceptor(); + + protected void assertProviderTrace(SofaRequest sofaRequest, Object result) { + assertTrace(false, sofaRequest, result); + } + + protected void assertConsumerTrace(SofaRequest sofaRequest, Object result) { + assertTrace(true, sofaRequest, result); + } + + private void assertTrace(boolean isClientSide, SofaRequest sofaRequest, Object result) { + ReportSpan lastSpan = MockEaseAgent.getLastSpan(); + Assert.assertNotNull(lastSpan); + Assert.assertNull(lastSpan.parentId()); + Assert.assertEquals(ConfigConst.Namespace.SOFARPC, lastSpan.remoteServiceName()); + Assert.assertEquals(SofaRpcCtxUtils.name(sofaRequest).toLowerCase(), lastSpan.name()); + if (isClientSide) { + Assert.assertEquals(Span.Kind.CLIENT.name(), lastSpan.kind()); + Assert.assertEquals(consumerConfig.getAppName(), lastSpan.tag(SofaRpcTraceTags.CLIENT_APPLICATION.name)); + Assert.assertEquals(rpcContext.getProviderInfo().getHost(), lastSpan.remoteEndpoint().ipv4()); + Assert.assertEquals(rpcContext.getProviderInfo().getPort(), lastSpan.remoteEndpoint().port()); + Assert.assertEquals(SofaRpcCtxUtils.method(sofaRequest), lastSpan.tag(SofaRpcTraceTags.METHOD.name)); + if (SofaRpcConsumerTraceInterceptor.SOFA_RPC_TRACE_CONFIG.argsCollectEnabled()) { + Assert.assertEquals(JsonUtil.toJson(sofaRequest.getMethodArgs()), lastSpan.tag(SofaRpcTraceTags.ARGS.name)); + } + if (result instanceof Throwable) { + Assert.assertTrue(lastSpan.hasError()); + Assert.assertNull(lastSpan.tag(SofaRpcTraceTags.RESULT.name)); + Assert.assertEquals(lastSpan.errorInfo(), ((Throwable) result).getMessage()); + } else if (SofaRpcConsumerTraceInterceptor.SOFA_RPC_TRACE_CONFIG.resultCollectEnabled()) { + Assert.assertFalse(lastSpan.hasError()); + Assert.assertEquals(JsonUtil.toJson(result), lastSpan.tag(SofaRpcTraceTags.RESULT.name)); + } + } else { + Assert.assertEquals(Span.Kind.SERVER.name(), lastSpan.kind()); + Assert.assertEquals(providerConfig.getAppName(), lastSpan.tag(SofaRpcTraceTags.SERVER_APPLICATION.name)); + Assert.assertEquals(rpcContext.getRemoteAddress().getHostString(), lastSpan.remoteEndpoint().ipv4()); + Assert.assertEquals(rpcContext.getRemoteAddress().getPort(), lastSpan.remoteEndpoint().port()); + } + + } +} diff --git a/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/MockBoltResponseFuture.java b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/MockBoltResponseFuture.java new file mode 100644 index 00000000..d13cdf23 --- /dev/null +++ b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/MockBoltResponseFuture.java @@ -0,0 +1,18 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor; + +import com.megaease.easeagent.plugin.field.DynamicFieldAccessor; + +public class MockBoltResponseFuture implements DynamicFieldAccessor { + private Object data; + + @Override + public void setEaseAgent$$DynamicField$$Data(Object data) { + this.data = data; + } + + @Override + public Object getEaseAgent$$DynamicField$$Data() { + return data; + } + +} diff --git a/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/BaseMetricsInterceptorTest.java b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/BaseMetricsInterceptorTest.java new file mode 100644 index 00000000..11946fce --- /dev/null +++ b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/BaseMetricsInterceptorTest.java @@ -0,0 +1,116 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.metrics; + +import com.alipay.sofa.rpc.common.RpcConstants; +import com.alipay.sofa.rpc.config.ConsumerConfig; +import com.alipay.sofa.rpc.config.ProviderConfig; +import com.alipay.sofa.rpc.context.RpcInternalContext; +import com.alipay.sofa.rpc.core.request.SofaRequest; +import com.alipay.sofa.rpc.filter.ConsumerInvoker; +import com.alipay.sofa.rpc.filter.ProviderInvoker; +import com.megaease.easeagent.mock.plugin.api.MockEaseAgent; +import com.megaease.easeagent.mock.plugin.api.utils.InterceptorTestUtils; +import com.megaease.easeagent.mock.plugin.api.utils.TagVerifier; +import com.megaease.easeagent.mock.report.impl.LastJsonReporter; +import com.megaease.easeagent.plugin.api.metric.name.MetricField; +import com.megaease.easeagent.plugin.api.metric.name.Tags; +import com.megaease.easeagent.plugin.enums.Order; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcMetricsTags; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcPlugin; +import org.junit.*; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.lang.reflect.Method; +import java.util.Map; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.when; + +public abstract class BaseMetricsInterceptorTest { + + protected RpcInternalContext rpcContext = RpcInternalContext.getContext(); + @Mock + protected SofaRequest sofaRequest; + @Mock + protected ConsumerInvoker consumerInvoker; + @Mock + protected ConsumerConfig consumerConfig; + @Mock + protected ProviderInvoker providerInvoker; + @Mock + protected ProviderConfig providerConfig; + @Mock + protected Method mockMethod; + protected Object[] allArguments; + private AutoCloseable autoCloseable; + + @BeforeClass + public static void beforeClass() { + } + + @AfterClass + public static void afterClass() { + } + + @Test + public void testGetType() { + String type = getInterceptor().getType(); + Assert.assertEquals(Order.METRIC.getName(), type); + } + + @Test + public void testOrder() { + int order = getInterceptor().order(); + Assert.assertEquals(Order.METRIC.getOrder(), order); + } + @After + public void destroy() throws Exception { + autoCloseable.close(); + } + + @Before + public void init() { + autoCloseable = MockitoAnnotations.openMocks(this); + when(consumerInvoker.getConfig()).thenReturn(consumerConfig); + when(consumerConfig.getAppName()).thenReturn("sofa-client"); + + when(providerInvoker.getConfig()).thenReturn(providerConfig); + when(providerConfig.getAppName()).thenReturn("sofa-server"); + + when(sofaRequest.getMethod()).thenReturn(mockMethod); + @SuppressWarnings("unchecked") + Class declaringClass = (Class) mockMethod.getDeclaringClass(); + when(declaringClass).thenReturn(BaseMetricsInterceptorTest.class); + when(mockMethod.getName()).thenReturn("mock"); + when(mockMethod.getParameterTypes()).thenReturn(new Class[]{String.class, Integer.class}); + when(sofaRequest.getInvokeType()).thenReturn(RpcConstants.INVOKER_TYPE_SYNC); + when(sofaRequest.getMethodArgs()).thenReturn(new Object[]{"abc", 3}); + + allArguments = new Object[]{sofaRequest}; + + SofaRpcPlugin sofaRpcPlugin = new SofaRpcPlugin(); + SofaRpcMetricsBaseInterceptor sofaRpcMetricsBaseInterceptor = getInterceptor(); + InterceptorTestUtils.init(sofaRpcMetricsBaseInterceptor, sofaRpcPlugin); + } + + protected abstract SofaRpcMetricsBaseInterceptor getInterceptor(); + + protected void assertMetrics(SofaRequest sofaRequest, Object result) { + TagVerifier tagVerifier = new TagVerifier() + .add(Tags.CATEGORY, SofaRpcMetricsTags.CATEGORY.name) + .add(Tags.TYPE, SofaRpcMetricsTags.TYPE.name) + .add(SofaRpcMetricsTags.LABEL_NAME.name, SofaRpcCtxUtils.methodSignature(sofaRequest)); + LastJsonReporter lastJsonReporter = MockEaseAgent.lastMetricJsonReporter(tagVerifier::verifyAnd); + Map metrics = lastJsonReporter.flushAndOnlyOne(); + + assertEquals(1, metrics.get(MetricField.EXECUTION_COUNT.getField())); + if (result instanceof Throwable) { + assertEquals(1, metrics.get(MetricField.EXECUTION_ERROR_COUNT.getField())); + } + } + + private static double metricValue(Map metrics, MetricField metricField) { + return (double) metrics.get(metricField.getField()); + } +} diff --git a/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/callback/MockSofaResponseCallback.java b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/callback/MockSofaResponseCallback.java new file mode 100644 index 00000000..f3ae9c78 --- /dev/null +++ b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/callback/MockSofaResponseCallback.java @@ -0,0 +1,28 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.callback; + +import com.alipay.sofa.rpc.core.exception.SofaRpcException; +import com.alipay.sofa.rpc.core.invoke.SofaResponseCallback; +import com.alipay.sofa.rpc.core.request.RequestBase; + +public class MockSofaResponseCallback implements SofaResponseCallback { + private Object result; + + public Object getResult() { + return result; + } + + @Override + public void onAppResponse(Object appResponse, String methodName, RequestBase request) { + this.result = appResponse; + } + + @Override + public void onAppException(Throwable throwable, String methodName, RequestBase request) { + this.result = throwable; + } + + @Override + public void onSofaException(SofaRpcException sofaException, String methodName, RequestBase request) { + this.result = sofaException; + } +} diff --git a/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/callback/SofaRpcResponseCallbackMetricsInterceptorTest.java b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/callback/SofaRpcResponseCallbackMetricsInterceptorTest.java new file mode 100644 index 00000000..e88b07cd --- /dev/null +++ b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/callback/SofaRpcResponseCallbackMetricsInterceptorTest.java @@ -0,0 +1,162 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.callback; + +import com.alipay.sofa.rpc.client.ProviderInfo; +import com.alipay.sofa.rpc.common.RpcConstants; +import com.alipay.sofa.rpc.core.exception.SofaTimeOutException; +import com.megaease.easeagent.mock.plugin.api.junit.EaseAgentJunit4ClassRunner; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.api.context.AsyncContext; +import com.megaease.easeagent.plugin.bridge.EaseAgent; +import com.megaease.easeagent.plugin.field.AgentFieldReflectAccessor; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.BaseMetricsInterceptorTest; +import com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.SofaRpcMetricsBaseInterceptor; +import com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.common.SofaRpcMetricsInterceptor; +import lombok.SneakyThrows; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.when; + +@RunWith(EaseAgentJunit4ClassRunner.class) +public class SofaRpcResponseCallbackMetricsInterceptorTest extends BaseMetricsInterceptorTest { + private final SofaRpcResponseCallbackMetricsInterceptor sofaRpcResponseCallbackMetricsInterceptor = new SofaRpcResponseCallbackMetricsInterceptor(); + private final SofaRpcMetricsInterceptor sofaRpcMetricsInterceptor = new SofaRpcMetricsInterceptor(); + private final MockSofaResponseCallback mockSofaResponseCallback = new MockSofaResponseCallback(); + private final Object[] responseCallbackMetricsInterceptorArgs = new Object[6]; + + + @Override + protected SofaRpcMetricsBaseInterceptor getInterceptor() { + return sofaRpcResponseCallbackMetricsInterceptor; + } + + @Before + public void setup() { + when(sofaRequest.getInvokeType()).thenReturn(RpcConstants.INVOKER_TYPE_CALLBACK); + + responseCallbackMetricsInterceptorArgs[2] = mockSofaResponseCallback; + ProviderInfo providerInfo = new ProviderInfo(); + providerInfo.setHost("127.0.0.1"); + providerInfo.setPort(12200); + rpcContext.setProviderInfo(providerInfo); + } + + @SneakyThrows + @Test + public void testConsumerCallbackSuccess() { + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + + Context context = EaseAgent.getContext(); + sofaRpcMetricsInterceptor.before(methodInfo, context); + sofaRpcMetricsInterceptor.after(methodInfo, context); + methodInfo.setArgs(responseCallbackMetricsInterceptorArgs); + sofaRpcResponseCallbackMetricsInterceptor.before(methodInfo, context); + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + SofaRpcResponseCallbackMetrics sofaRpcResponseCallbackMetrics = (SofaRpcResponseCallbackMetrics) methodInfo.getArgs()[2]; + sofaRpcResponseCallbackMetrics.onAppResponse("success", sofaRequest.getMethod().getName(), sofaRequest); + } + }); + asyncThread.start(); + asyncThread.join(); + + assertMetrics(sofaRequest, mockSofaResponseCallback.getResult()); + } + + @SneakyThrows + @Test + public void testConsumerCallbackWithException() { + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + + Context context = EaseAgent.getContext(); + sofaRpcMetricsInterceptor.before(methodInfo, context); + sofaRpcMetricsInterceptor.after(methodInfo, context); + methodInfo.setArgs(responseCallbackMetricsInterceptorArgs); + sofaRpcResponseCallbackMetricsInterceptor.before(methodInfo, context); + + Throwable throwable = new Throwable("call exception"); + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + SofaRpcResponseCallbackMetrics sofaRpcResponseCallbackMetrics = (SofaRpcResponseCallbackMetrics) methodInfo.getArgs()[2]; + sofaRpcResponseCallbackMetrics.onAppException(throwable, sofaRequest.getMethod().getName(), sofaRequest); + } + }); + asyncThread.start(); + asyncThread.join(); + + assertMetrics(sofaRequest, mockSofaResponseCallback.getResult()); + } + + @SneakyThrows + @Test + public void testConsumerCallbackWithSofaException() { + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + + Context context = EaseAgent.getContext(); + sofaRpcMetricsInterceptor.before(methodInfo, context); + sofaRpcMetricsInterceptor.after(methodInfo, context); + methodInfo.setArgs(responseCallbackMetricsInterceptorArgs); + sofaRpcResponseCallbackMetricsInterceptor.before(methodInfo, context); + + SofaTimeOutException sofaTimeOutException = new SofaTimeOutException("call timeout"); + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + SofaRpcResponseCallbackMetrics sofaRpcResponseCallbackMetrics = (SofaRpcResponseCallbackMetrics) methodInfo.getArgs()[2]; + sofaRpcResponseCallbackMetrics.onSofaException(sofaTimeOutException, sofaRequest.getMethod().getName(), sofaRequest); + } + }); + asyncThread.start(); + asyncThread.join(); + + assertMetrics(sofaRequest, mockSofaResponseCallback.getResult()); + } + + @SneakyThrows + @Test + public void testConsumerCallbackNoObtainedMethodFullName() { + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + + Context context = EaseAgent.getContext(); + sofaRpcMetricsInterceptor.before(methodInfo, context); + sofaRpcMetricsInterceptor.after(methodInfo, context); + methodInfo.setArgs(responseCallbackMetricsInterceptorArgs); + sofaRpcResponseCallbackMetricsInterceptor.before(methodInfo, context); + + SofaTimeOutException sofaTimeOutException = new SofaTimeOutException("call timeout"); + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + SofaRpcResponseCallbackMetrics sofaRpcResponseCallbackMetrics = (SofaRpcResponseCallbackMetrics) methodInfo.getArgs()[2]; + AsyncContext asyncContext = AgentFieldReflectAccessor.getFieldValue(sofaRpcResponseCallbackMetrics, "asyncContext"); + Assert.assertNotNull(asyncContext); + asyncContext.put(SofaRpcCtxUtils.METRICS_KEY_NAME, null); + sofaRpcResponseCallbackMetrics.onSofaException(sofaTimeOutException, sofaRequest.getMethod().getName(), sofaRequest); + } + }); + asyncThread.start(); + asyncThread.join(); + } +} diff --git a/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/common/SofaRpcMetricsInterceptorTest.java b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/common/SofaRpcMetricsInterceptorTest.java new file mode 100644 index 00000000..783cfe08 --- /dev/null +++ b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/common/SofaRpcMetricsInterceptorTest.java @@ -0,0 +1,98 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.common; + +import com.alipay.sofa.rpc.client.ProviderInfo; +import com.alipay.sofa.rpc.common.RpcConstants; +import com.alipay.sofa.rpc.core.exception.SofaTimeOutException; +import com.alipay.sofa.rpc.core.response.SofaResponse; +import com.megaease.easeagent.mock.plugin.api.junit.EaseAgentJunit4ClassRunner; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.bridge.EaseAgent; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.BaseMetricsInterceptorTest; +import com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.SofaRpcMetricsBaseInterceptor; +import lombok.SneakyThrows; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.when; + +@RunWith(EaseAgentJunit4ClassRunner.class) +public class SofaRpcMetricsInterceptorTest extends BaseMetricsInterceptorTest { + private final SofaRpcMetricsInterceptor sofaRpcMetricsInterceptor = new SofaRpcMetricsInterceptor(); + + @Override + protected SofaRpcMetricsBaseInterceptor getInterceptor() { + return sofaRpcMetricsInterceptor; + } + + @Before + public void setUp() { + when(sofaRequest.getInvokeType()).thenReturn(RpcConstants.INVOKER_TYPE_SYNC); + + ProviderInfo providerInfo = new ProviderInfo(); + providerInfo.setHost("127.0.0.1"); + providerInfo.setPort(12200); + rpcContext.setProviderInfo(providerInfo); + } + + + @Test + @SneakyThrows + public void testSuccess() { + SofaResponse sofaResponse = new SofaResponse(); + sofaResponse.setAppResponse("success"); + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .retValue(sofaResponse) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcMetricsInterceptor.before(methodInfo, context); + sofaRpcMetricsInterceptor.after(methodInfo, context); + + assertMetrics(sofaRequest, sofaResponse.getAppResponse()); + } + + @Test + @SneakyThrows + public void testWithResultHasException() { + SofaTimeOutException timeoutException = new SofaTimeOutException("call timeout"); + SofaResponse sofaResponse = new SofaResponse(); + sofaResponse.setAppResponse(timeoutException); + sofaResponse.setErrorMsg(timeoutException.getMessage()); + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .retValue(sofaResponse) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcMetricsInterceptor.before(methodInfo, context); + sofaRpcMetricsInterceptor.after(methodInfo, context); + + assertMetrics(sofaRequest, sofaResponse.getAppResponse()); + } + + @Test + @SneakyThrows + public void testWithExecuteException() { + Throwable executeException = new Throwable("method execute exception"); + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .throwable(executeException) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcMetricsInterceptor.before(methodInfo, context); + sofaRpcMetricsInterceptor.after(methodInfo, context); + + assertMetrics(sofaRequest, executeException); + } + +} \ No newline at end of file diff --git a/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/future/SofaRpcResponseFutureMetricsInterceptorTest.java b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/future/SofaRpcResponseFutureMetricsInterceptorTest.java new file mode 100644 index 00000000..a7ba759d --- /dev/null +++ b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/metrics/future/SofaRpcResponseFutureMetricsInterceptorTest.java @@ -0,0 +1,143 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.future; + +import com.alipay.sofa.rpc.client.ProviderInfo; +import com.alipay.sofa.rpc.common.RpcConstants; +import com.megaease.easeagent.mock.plugin.api.junit.EaseAgentJunit4ClassRunner; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.bridge.EaseAgent; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.interceptor.MockBoltResponseFuture; +import com.megaease.easeagent.plugin.sofarpc.interceptor.initalize.SofaRpcFutureInvokeCallbackConstructInterceptor; +import com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.BaseMetricsInterceptorTest; +import com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.SofaRpcMetricsBaseInterceptor; +import com.megaease.easeagent.plugin.sofarpc.interceptor.metrics.common.SofaRpcMetricsInterceptor; +import lombok.SneakyThrows; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.when; + +@RunWith(EaseAgentJunit4ClassRunner.class) +public class SofaRpcResponseFutureMetricsInterceptorTest extends BaseMetricsInterceptorTest { + private final SofaRpcMetricsInterceptor sofaRpcMetricsInterceptor = new SofaRpcMetricsInterceptor(); + private final SofaRpcResponseFutureMetricsInterceptor sofaRpcResponseFutureMetricsInterceptor = new SofaRpcResponseFutureMetricsInterceptor(); + private final SofaRpcFutureInvokeCallbackConstructInterceptor sofaRpcFutureInvokeCallbackConstructInterceptor = new SofaRpcFutureInvokeCallbackConstructInterceptor(); + private final Object[] futureInvokeCallbackConstructMetricsInterceptorArgs = new Object[6]; + + @Override + protected SofaRpcMetricsBaseInterceptor getInterceptor() { + return sofaRpcResponseFutureMetricsInterceptor; + } + + @Before + public void setup() { + futureInvokeCallbackConstructMetricsInterceptorArgs[2] = new MockBoltResponseFuture(); + when(sofaRequest.getInvokeType()).thenReturn(RpcConstants.INVOKER_TYPE_FUTURE); + + ProviderInfo providerInfo = new ProviderInfo(); + providerInfo.setHost("127.0.0.1"); + providerInfo.setPort(12200); + rpcContext.setProviderInfo(providerInfo); + } + + + @Test + public void testConsumerFutureInvokeSuccess() throws InterruptedException { + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcMetricsInterceptor.before(methodInfo, context); + sofaRpcMetricsInterceptor.after(methodInfo, context); + methodInfo.setArgs(futureInvokeCallbackConstructMetricsInterceptorArgs); + sofaRpcFutureInvokeCallbackConstructInterceptor.before(methodInfo,context); + String result = "success"; + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + methodInfo.setInvoker(futureInvokeCallbackConstructMetricsInterceptorArgs[2]); + methodInfo.setArgs(new Object[]{result}); + sofaRpcResponseFutureMetricsInterceptor.after(methodInfo, context); + } + }); + asyncThread.start(); + asyncThread.join(); + + assertMetrics(sofaRequest, result); + } + + @Test + @SneakyThrows + public void testConsumerFutureInvokeFailure() { + Throwable throwable = new Throwable("Unknown Exception"); + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcMetricsInterceptor.before(methodInfo, context); + sofaRpcMetricsInterceptor.after(methodInfo, context); + methodInfo.setArgs(futureInvokeCallbackConstructMetricsInterceptorArgs); + sofaRpcFutureInvokeCallbackConstructInterceptor.before(methodInfo,context); + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + methodInfo.setInvoker(futureInvokeCallbackConstructMetricsInterceptorArgs[2]); + methodInfo.setArgs(new Object[]{throwable}); + sofaRpcResponseFutureMetricsInterceptor.after(methodInfo, context); + } + }); + asyncThread.start(); + asyncThread.join(); + + assertMetrics(sofaRequest, throwable); + } + + @Test + @SneakyThrows + public void testConsumerFutureInvokeException() { + Throwable throwable = new Throwable("Unknown Exception"); + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcMetricsInterceptor.before(methodInfo, context); + sofaRpcMetricsInterceptor.after(methodInfo, context); + methodInfo.setArgs(futureInvokeCallbackConstructMetricsInterceptorArgs); + sofaRpcFutureInvokeCallbackConstructInterceptor.before(methodInfo,context); + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + methodInfo.throwable(throwable); + methodInfo.setInvoker(futureInvokeCallbackConstructMetricsInterceptorArgs[2]); + sofaRpcResponseFutureMetricsInterceptor.after(methodInfo, context); + } + }); + asyncThread.start(); + asyncThread.join(); + + assertMetrics(sofaRequest, throwable); + } + + @Test(expected = NullPointerException.class) + public void testNotObtainedAsyncContext() { + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + Context context = EaseAgent.getContext(); + methodInfo.setInvoker(futureInvokeCallbackConstructMetricsInterceptorArgs[2]); + sofaRpcResponseFutureMetricsInterceptor.after(methodInfo, context); + } +} diff --git a/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/callback/MockSofaResponseCallback.java b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/callback/MockSofaResponseCallback.java new file mode 100644 index 00000000..5a913c72 --- /dev/null +++ b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/callback/MockSofaResponseCallback.java @@ -0,0 +1,28 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.trace.callback; + +import com.alipay.sofa.rpc.core.exception.SofaRpcException; +import com.alipay.sofa.rpc.core.invoke.SofaResponseCallback; +import com.alipay.sofa.rpc.core.request.RequestBase; + +public class MockSofaResponseCallback implements SofaResponseCallback { + private Object result; + + public Object getResult() { + return result; + } + + @Override + public void onAppResponse(Object appResponse, String methodName, RequestBase request) { + this.result = appResponse; + } + + @Override + public void onAppException(Throwable throwable, String methodName, RequestBase request) { + this.result = throwable; + } + + @Override + public void onSofaException(SofaRpcException sofaException, String methodName, RequestBase request) { + this.result = sofaException; + } +} diff --git a/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/callback/SofaRpcResponseCallbackTraceInterceptorTest.java b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/callback/SofaRpcResponseCallbackTraceInterceptorTest.java new file mode 100644 index 00000000..9eb615b9 --- /dev/null +++ b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/callback/SofaRpcResponseCallbackTraceInterceptorTest.java @@ -0,0 +1,189 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.trace.callback; + +import com.alipay.sofa.rpc.client.ProviderInfo; +import com.alipay.sofa.rpc.common.RpcConstants; +import com.alipay.sofa.rpc.core.exception.SofaTimeOutException; +import com.megaease.easeagent.mock.plugin.api.junit.EaseAgentJunit4ClassRunner; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.api.context.AsyncContext; +import com.megaease.easeagent.plugin.api.context.ContextUtils; +import com.megaease.easeagent.plugin.api.context.RequestContext; +import com.megaease.easeagent.plugin.bridge.EaseAgent; +import com.megaease.easeagent.plugin.field.AgentFieldReflectAccessor; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.interceptor.BaseInterceptorTest; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.SofaRpcTraceBaseInterceptor; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.common.SofaRpcConsumerTraceInterceptor; +import lombok.SneakyThrows; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.when; + +@RunWith(EaseAgentJunit4ClassRunner.class) +public class SofaRpcResponseCallbackTraceInterceptorTest extends BaseInterceptorTest { + private final SofaRpcResponseCallbackTraceInterceptor sofaRpcResponseCallbackTraceInterceptor = new SofaRpcResponseCallbackTraceInterceptor(); + private final SofaRpcConsumerTraceInterceptor sofaRpcConsumerTraceInterceptor = new SofaRpcConsumerTraceInterceptor(); + private final MockSofaResponseCallback mockSofaResponseCallback = new MockSofaResponseCallback(); + private final Object[] responseCallbackTraceInterceptorArgs = new Object[6]; + + + @Override + protected SofaRpcTraceBaseInterceptor getInterceptor() { + return sofaRpcResponseCallbackTraceInterceptor; + } + + @Before + public void setup() { + when(sofaRequest.getInvokeType()).thenReturn(RpcConstants.INVOKER_TYPE_CALLBACK); + + responseCallbackTraceInterceptorArgs[2] = mockSofaResponseCallback; + ProviderInfo providerInfo = new ProviderInfo(); + providerInfo.setHost("127.0.0.1"); + providerInfo.setPort(12200); + rpcContext.setProviderInfo(providerInfo); + } + + @SneakyThrows + @Test + public void testConsumerCallbackSuccess() { + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + + Context context = EaseAgent.getContext(); + sofaRpcConsumerTraceInterceptor.before(methodInfo, context); + sofaRpcConsumerTraceInterceptor.after(methodInfo,context); + methodInfo.setArgs(responseCallbackTraceInterceptorArgs); + sofaRpcResponseCallbackTraceInterceptor.before(methodInfo, context); + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + SofaRpcResponseCallbackTrace sofaRpcResponseCallbackTrace = (SofaRpcResponseCallbackTrace) methodInfo.getArgs()[2]; + sofaRpcResponseCallbackTrace.onAppResponse("success", sofaRequest.getMethod().getName(), sofaRequest); + } + }); + asyncThread.start(); + asyncThread.join(); + + assertConsumerTrace(sofaRequest, mockSofaResponseCallback.getResult()); + } + + @SneakyThrows + @Test + public void testConsumerCallbackWithException() { + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + + Context context = EaseAgent.getContext(); + sofaRpcConsumerTraceInterceptor.before(methodInfo, context); + methodInfo.setArgs(responseCallbackTraceInterceptorArgs); + sofaRpcResponseCallbackTraceInterceptor.before(methodInfo, context); + + Throwable throwable = new Throwable("call exception"); + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + SofaRpcResponseCallbackTrace sofaRpcResponseCallbackTrace = (SofaRpcResponseCallbackTrace) methodInfo.getArgs()[2]; + sofaRpcResponseCallbackTrace.onAppException(throwable, sofaRequest.getMethod().getName(), sofaRequest); + } + }); + asyncThread.start(); + asyncThread.join(); + + assertConsumerTrace(sofaRequest, mockSofaResponseCallback.getResult()); + } + + @SneakyThrows + @Test + public void testConsumerCallbackWithSofaException() { + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + + Context context = EaseAgent.getContext(); + sofaRpcConsumerTraceInterceptor.before(methodInfo, context); + methodInfo.setArgs(responseCallbackTraceInterceptorArgs); + sofaRpcResponseCallbackTraceInterceptor.before(methodInfo, context); + + SofaTimeOutException sofaTimeOutException = new SofaTimeOutException("call timeout"); + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + SofaRpcResponseCallbackTrace sofaRpcResponseCallbackTrace = (SofaRpcResponseCallbackTrace) methodInfo.getArgs()[2]; + sofaRpcResponseCallbackTrace.onSofaException(sofaTimeOutException, sofaRequest.getMethod().getName(), sofaRequest); + } + }); + asyncThread.start(); + asyncThread.join(); + + assertConsumerTrace(sofaRequest, mockSofaResponseCallback.getResult()); + } + + + @Test(expected = NullPointerException.class) + public void testConsumerNoObtainedRequestContext() { + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcConsumerTraceInterceptor.before(methodInfo, context); + methodInfo.setArgs(responseCallbackTraceInterceptorArgs); + RequestContext requestContext = ContextUtils.removeFromContext(context, SofaRpcCtxUtils.CLIENT_REQUEST_CONTEXT_KEY); + requestContext.span().finish(); + requestContext.scope().close(); + sofaRpcResponseCallbackTraceInterceptor.before(methodInfo, context); + + } + + + @SneakyThrows + @Test + public void testConsumerCallbackNoObtainedRequestContext() { + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + + Context context = EaseAgent.getContext(); + sofaRpcConsumerTraceInterceptor.before(methodInfo, context); + methodInfo.setArgs(responseCallbackTraceInterceptorArgs); + sofaRpcResponseCallbackTraceInterceptor.before(methodInfo, context); + + SofaTimeOutException sofaTimeOutException = new SofaTimeOutException("call timeout"); + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + SofaRpcResponseCallbackTrace sofaRpcResponseCallbackTrace = (SofaRpcResponseCallbackTrace) methodInfo.getArgs()[2]; + AsyncContext asyncContext = AgentFieldReflectAccessor.getFieldValue(sofaRpcResponseCallbackTrace, "asyncContext"); + Assert.assertNotNull(asyncContext); + RequestContext requestContext = asyncContext.get(SofaRpcCtxUtils.CLIENT_REQUEST_CONTEXT_KEY); + asyncContext.put(SofaRpcCtxUtils.CLIENT_REQUEST_CONTEXT_KEY, null); + requestContext.span().finish(); + requestContext.scope().close(); + try { + sofaRpcResponseCallbackTrace.onSofaException(sofaTimeOutException, sofaRequest.getMethod().getName(), sofaRequest); + } catch (NullPointerException ignore) { + //Must be throw NullPointerException + return; + } + throw new RuntimeException("Must be throw NullPointerException"); + } + }); + asyncThread.start(); + asyncThread.join(); + } +} diff --git a/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaRpcConsumerTraceInterceptorTest.java b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaRpcConsumerTraceInterceptorTest.java new file mode 100644 index 00000000..6db88000 --- /dev/null +++ b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaRpcConsumerTraceInterceptorTest.java @@ -0,0 +1,112 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.trace.common; + +import com.alipay.sofa.rpc.client.ProviderInfo; +import com.alipay.sofa.rpc.common.RpcConstants; +import com.alipay.sofa.rpc.core.response.SofaResponse; +import com.megaease.easeagent.mock.plugin.api.junit.EaseAgentJunit4ClassRunner; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.api.context.RequestContext; +import com.megaease.easeagent.plugin.bridge.EaseAgent; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.interceptor.BaseInterceptorTest; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.SofaRpcTraceBaseInterceptor; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.when; + +@RunWith(EaseAgentJunit4ClassRunner.class) +public class SofaRpcConsumerTraceInterceptorTest extends BaseInterceptorTest { + private final SofaRpcConsumerTraceInterceptor consumerTraceInterceptor = new SofaRpcConsumerTraceInterceptor(); + + @Override + protected SofaRpcTraceBaseInterceptor getInterceptor() { + return consumerTraceInterceptor; + } + + @Before + public void setUp() { + when(sofaRequest.getInvokeType()).thenReturn(RpcConstants.INVOKER_TYPE_SYNC); + + ProviderInfo providerInfo = new ProviderInfo(); + providerInfo.setHost("127.0.0.1"); + providerInfo.setPort(12200); + rpcContext.setProviderInfo(providerInfo); + } + + + @Test + public void testConsumerSuccess() { + SofaResponse sofaResponse = new SofaResponse(); + sofaResponse.setAppResponse("success"); + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .retValue(sofaResponse) + .build(); + + Context context = EaseAgent.getContext(); + consumerTraceInterceptor.before(methodInfo, context); + consumerTraceInterceptor.after(methodInfo, context); + + assertConsumerTrace(sofaRequest, sofaResponse.getAppResponse()); + } + + @Test + public void testConsumerWithResultHasException() { + RuntimeException runtimeException = new RuntimeException("call timeout"); + SofaResponse sofaResponse = new SofaResponse(); + sofaResponse.setAppResponse(runtimeException); + sofaResponse.setErrorMsg(runtimeException.getMessage()); + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .retValue(sofaResponse) + .build(); + + Context context = EaseAgent.getContext(); + consumerTraceInterceptor.before(methodInfo, context); + consumerTraceInterceptor.after(methodInfo, context); + + assertConsumerTrace(sofaRequest, sofaResponse.getAppResponse()); + } + + @Test + public void testConsumerWithExecuteException() { + Throwable executeException = new Throwable("method execute exception"); + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .throwable(executeException) + .build(); + + Context context = EaseAgent.getContext(); + consumerTraceInterceptor.before(methodInfo, context); + consumerTraceInterceptor.after(methodInfo, context); + + assertConsumerTrace(sofaRequest, executeException); + } + + + @Test(expected = NullPointerException.class) + public void testConsumerNoObtainedRequestContext() { + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + Context context = EaseAgent.getContext(); + consumerTraceInterceptor.before(methodInfo, context); + RequestContext requestContext = context.remove(SofaRpcCtxUtils.CLIENT_REQUEST_CONTEXT_KEY); + requestContext.span().finish(); + requestContext.scope().close(); + consumerTraceInterceptor.after(methodInfo, context); + } + +} \ No newline at end of file diff --git a/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaRpcProviderTraceInterceptorTest.java b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaRpcProviderTraceInterceptorTest.java new file mode 100644 index 00000000..f019b4fb --- /dev/null +++ b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/common/SofaRpcProviderTraceInterceptorTest.java @@ -0,0 +1,103 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.trace.common; + +import com.alipay.sofa.rpc.core.response.SofaResponse; +import com.megaease.easeagent.mock.plugin.api.junit.EaseAgentJunit4ClassRunner; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.api.context.ContextUtils; +import com.megaease.easeagent.plugin.api.context.RequestContext; +import com.megaease.easeagent.plugin.bridge.EaseAgent; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.interceptor.BaseInterceptorTest; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.SofaRpcTraceBaseInterceptor; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(EaseAgentJunit4ClassRunner.class) +public class SofaRpcProviderTraceInterceptorTest extends BaseInterceptorTest { + + private final SofaRpcProviderTraceInterceptor sofaRpcProviderTraceInterceptor = new SofaRpcProviderTraceInterceptor(); + + @Override + protected SofaRpcTraceBaseInterceptor getInterceptor() { + return sofaRpcProviderTraceInterceptor; + } + + @Before + public void setUp() { + rpcContext.setRemoteAddress("127.0.0.1",12200); + } + + + @Test + public void testProviderSuccess() { + SofaResponse sofaResponse = new SofaResponse(); + sofaResponse.setAppResponse("success"); + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(providerInvoker) + .args(allArguments) + .retValue(sofaResponse) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcProviderTraceInterceptor.before(methodInfo, context); + sofaRpcProviderTraceInterceptor.after(methodInfo, context); + + assertProviderTrace(sofaRequest, sofaResponse.getAppResponse()); + } + + @Test + public void testProviderWithResultHasException() { + RuntimeException runtimeException = new RuntimeException("call exception"); + SofaResponse sofaResponse = new SofaResponse(); + sofaResponse.setAppResponse(runtimeException); + sofaResponse.setErrorMsg(runtimeException.getMessage()); + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(providerInvoker) + .args(allArguments) + .retValue(sofaResponse) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcProviderTraceInterceptor.before(methodInfo, context); + sofaRpcProviderTraceInterceptor.after(methodInfo, context); + + assertProviderTrace(sofaRequest, sofaResponse.getAppResponse()); + } + + @Test + public void testProviderWithExecuteException() { + RuntimeException executeException = new RuntimeException("provider call exception"); + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(providerInvoker) + .args(allArguments) + .throwable(executeException) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcProviderTraceInterceptor.before(methodInfo, context); + sofaRpcProviderTraceInterceptor.after(methodInfo, context); + + assertProviderTrace(sofaRequest, executeException); + } + + @Test(expected = NullPointerException.class) + public void testProviderNoObtainedRequestContext() { + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(providerInvoker) + .args(allArguments) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcProviderTraceInterceptor.before(methodInfo, context); + RequestContext requestContext = ContextUtils.removeFromContext(context,SofaRpcCtxUtils.SERVER_REQUEST_CONTEXT_KEY); + requestContext.span().finish(); + requestContext.scope().close(); + sofaRpcProviderTraceInterceptor.after(methodInfo, context); + } +} diff --git a/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/future/SofaRpcResponseFutureTraceInterceptorTest.java b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/future/SofaRpcResponseFutureTraceInterceptorTest.java new file mode 100644 index 00000000..35f242a6 --- /dev/null +++ b/plugins/sofarpc/src/test/java/com/megaease/easeagent/plugin/sofarpc/interceptor/trace/future/SofaRpcResponseFutureTraceInterceptorTest.java @@ -0,0 +1,187 @@ +package com.megaease.easeagent.plugin.sofarpc.interceptor.trace.future; + +import com.alipay.sofa.rpc.client.ProviderInfo; +import com.alipay.sofa.rpc.common.RpcConstants; +import com.megaease.easeagent.mock.plugin.api.junit.EaseAgentJunit4ClassRunner; +import com.megaease.easeagent.plugin.api.Context; +import com.megaease.easeagent.plugin.api.context.AsyncContext; +import com.megaease.easeagent.plugin.api.context.RequestContext; +import com.megaease.easeagent.plugin.bridge.EaseAgent; +import com.megaease.easeagent.plugin.field.AgentDynamicFieldAccessor; +import com.megaease.easeagent.plugin.interceptor.MethodInfo; +import com.megaease.easeagent.plugin.sofarpc.SofaRpcCtxUtils; +import com.megaease.easeagent.plugin.sofarpc.interceptor.BaseInterceptorTest; +import com.megaease.easeagent.plugin.sofarpc.interceptor.MockBoltResponseFuture; +import com.megaease.easeagent.plugin.sofarpc.interceptor.initalize.SofaRpcFutureInvokeCallbackConstructInterceptor; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.SofaRpcTraceBaseInterceptor; +import com.megaease.easeagent.plugin.sofarpc.interceptor.trace.common.SofaRpcConsumerTraceInterceptor; +import lombok.SneakyThrows; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.when; + +@RunWith(EaseAgentJunit4ClassRunner.class) +public class SofaRpcResponseFutureTraceInterceptorTest extends BaseInterceptorTest { + private final SofaRpcConsumerTraceInterceptor sofaRpcConsumerTraceInterceptor = new SofaRpcConsumerTraceInterceptor(); + private final SofaRpcResponseFutureTraceInterceptor sofaRpcResponseFutureTraceInterceptor = new SofaRpcResponseFutureTraceInterceptor(); + private final SofaRpcFutureInvokeCallbackConstructInterceptor sofaRpcFutureInvokeCallbackConstructInterceptor = new SofaRpcFutureInvokeCallbackConstructInterceptor(); + private final Object[] futureInvokeCallbackConstructTracingInterceptorArgs = new Object[6]; + + + @Override + protected SofaRpcTraceBaseInterceptor getInterceptor() { + return sofaRpcResponseFutureTraceInterceptor; + } + + @Before + public void setup() { + futureInvokeCallbackConstructTracingInterceptorArgs[2] = new MockBoltResponseFuture(); + when(sofaRequest.getInvokeType()).thenReturn(RpcConstants.INVOKER_TYPE_FUTURE); + + ProviderInfo providerInfo = new ProviderInfo(); + providerInfo.setHost("127.0.0.1"); + providerInfo.setPort(12200); + rpcContext.setProviderInfo(providerInfo); + } + + + @Test + public void testConsumerFutureInvokeSuccess() throws InterruptedException { + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcConsumerTraceInterceptor.before(methodInfo, context); + sofaRpcConsumerTraceInterceptor.after(methodInfo, context); + methodInfo.setArgs(futureInvokeCallbackConstructTracingInterceptorArgs); + sofaRpcFutureInvokeCallbackConstructInterceptor.before(methodInfo, context); + String result = "success"; + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + methodInfo.setInvoker(futureInvokeCallbackConstructTracingInterceptorArgs[2]); + methodInfo.setArgs(new Object[]{result}); + sofaRpcResponseFutureTraceInterceptor.after(methodInfo, context); + } + }); + asyncThread.start(); + asyncThread.join(); + + assertConsumerTrace(sofaRequest, result); + } + + @Test + @SneakyThrows + public void testConsumerFutureInvokeFailure() { + Throwable throwable = new Throwable("Unknown Exception"); + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcConsumerTraceInterceptor.before(methodInfo, context); + sofaRpcConsumerTraceInterceptor.after(methodInfo, context); + methodInfo.setArgs(futureInvokeCallbackConstructTracingInterceptorArgs); + sofaRpcFutureInvokeCallbackConstructInterceptor.before(methodInfo, context); + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + methodInfo.setInvoker(futureInvokeCallbackConstructTracingInterceptorArgs[2]); + methodInfo.setArgs(new Object[]{throwable}); + sofaRpcResponseFutureTraceInterceptor.after(methodInfo, context); + } + }); + asyncThread.start(); + asyncThread.join(); + + assertConsumerTrace(sofaRequest, throwable); + } + + @Test + @SneakyThrows + public void testConsumerFutureInvokeException() { + IllegalStateException illegalStateException = new IllegalStateException("complete already"); + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcConsumerTraceInterceptor.before(methodInfo, context); + sofaRpcConsumerTraceInterceptor.after(methodInfo, context); + methodInfo.setArgs(futureInvokeCallbackConstructTracingInterceptorArgs); + sofaRpcFutureInvokeCallbackConstructInterceptor.before(methodInfo, context); + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + methodInfo.throwable(illegalStateException); + methodInfo.setInvoker(futureInvokeCallbackConstructTracingInterceptorArgs[2]); + sofaRpcResponseFutureTraceInterceptor.after(methodInfo, context); + } + }); + asyncThread.start(); + asyncThread.join(); + + assertConsumerTrace(sofaRequest, illegalStateException); + } + + @Test(expected = NullPointerException.class) + public void testNotObtainedAsyncContext() { + + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + Context context = EaseAgent.getContext(); + methodInfo.setInvoker(futureInvokeCallbackConstructTracingInterceptorArgs[2]); + sofaRpcResponseFutureTraceInterceptor.after(methodInfo, context); + } + + @Test + @SneakyThrows + public void testNotObtainedRequestContext() { + MethodInfo methodInfo = MethodInfo.builder() + .invoker(consumerInvoker) + .args(allArguments) + .build(); + + Context context = EaseAgent.getContext(); + sofaRpcConsumerTraceInterceptor.before(methodInfo, context); + sofaRpcConsumerTraceInterceptor.after(methodInfo, context); + methodInfo.setArgs(futureInvokeCallbackConstructTracingInterceptorArgs); + sofaRpcFutureInvokeCallbackConstructInterceptor.before(methodInfo, context); + MockBoltResponseFuture mockBoltResponseFuture = (MockBoltResponseFuture) futureInvokeCallbackConstructTracingInterceptorArgs[2]; + + Thread asyncThread = new Thread(new Runnable() { + @Override + public void run() { + AsyncContext asyncContext = AgentDynamicFieldAccessor.getDynamicFieldValue(mockBoltResponseFuture); + Assert.assertNotNull(asyncContext); + RequestContext requestContext = asyncContext.get(SofaRpcCtxUtils.CLIENT_REQUEST_CONTEXT_KEY); + requestContext.span().finish(); + asyncContext.put(SofaRpcCtxUtils.CLIENT_REQUEST_CONTEXT_KEY, null); + methodInfo.setInvoker(futureInvokeCallbackConstructTracingInterceptorArgs[2]); + try { + sofaRpcResponseFutureTraceInterceptor.after(methodInfo, context); + } catch (NullPointerException ignore) { + //Must be throw NullPointerException + return; + } + throw new RuntimeException("Must be throw NullPointerException"); + } + }); + asyncThread.start(); + asyncThread.join(); + } + +} diff --git a/plugins/sofarpc/src/test/resources/mock_agent.properties b/plugins/sofarpc/src/test/resources/mock_agent.properties new file mode 100644 index 00000000..9697e83d --- /dev/null +++ b/plugins/sofarpc/src/test/resources/mock_agent.properties @@ -0,0 +1,3 @@ +## sofarpc arguments collect switch +plugin.observability.sofarpc.tracing.args.collect.enabled=true +plugin.observability.sofarpc.tracing.result.collect.enabled=true diff --git a/pom.xml b/pom.xml index d65b8351..16c7beda 100644 --- a/pom.xml +++ b/pom.xml @@ -111,6 +111,12 @@ ${version.mockito} test + + org.mockito + mockito-inline + ${version.mockito} + test + org.awaitility awaitility