You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shenyu.apache.org by xi...@apache.org on 2022/06/19 14:11:36 UTC
[incubator-shenyu] branch master updated: [new : feature] add loggingElasticSearch plugin (#3580)
This is an automated email from the ASF dual-hosted git repository.
xiaoyu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 30e0f6213 [new : feature] add loggingElasticSearch plugin (#3580)
30e0f6213 is described below
commit 30e0f6213379b7dfcaaa8c6f746bc0366a5d17a5
Author: qinghai777 <80...@users.noreply.github.com>
AuthorDate: Sun Jun 19 22:11:31 2022 +0800
[new : feature] add loggingElasticSearch plugin (#3580)
* add loggingElasticSearch plugin
* modify es plugin
---
db/init/mysql/schema.sql | 9 +
db/init/oracle/schema.sql | 25 ++
db/init/pg/create-table.sql | 9 +
db/upgrade/2.4.3-upgrade-2.5.0-mysql.sql | 15 +-
db/upgrade/2.4.3-upgrade-2.5.0-pg.sql | 15 +-
pom.xml | 16 +
.../src/main/resources/sql-script/h2/schema.sql | 11 +-
shenyu-bootstrap/pom.xml | 8 +
.../org/apache/shenyu/common/enums/PluginEnum.java | 5 +
shenyu-plugin/shenyu-plugin-logging/pom.xml | 1 +
.../shenyu-plugin-logging-elasticsearch/pom.xml | 66 ++++
.../elasticsearch/AbstractLogCollector.java | 115 ++++++
.../logging/elasticsearch/DefaultLogCollector.java | 42 +++
.../plugin/logging/elasticsearch/LogCollector.java | 38 ++
.../logging/elasticsearch/LogConsumeClient.java | 36 ++
.../elasticsearch/LoggingElasticSearchPlugin.java | 93 +++++
.../logging/elasticsearch/body/BodyWriter.java | 107 ++++++
.../LoggingElasticSearchServerHttpRequest.java | 65 ++++
.../body/LoggingElasticSearchServerResponse.java | 254 +++++++++++++
.../elasticsearch/config/LogCollectConfig.java | 250 +++++++++++++
.../elasticsearch/constant/LoggingConstant.java | 32 ++
.../ElasticSearchLogCollectClient.java | 145 ++++++++
.../elasticsearch/entity/LZ4CompressData.java | 69 ++++
.../elasticsearch/entity/ShenyuRequestLog.java | 407 +++++++++++++++++++++
.../LoggingElasticSearchPluginDataHandler.java | 145 ++++++++
.../elasticsearch/sampler/CountSampler.java | 115 ++++++
.../logging/elasticsearch/sampler/Sampler.java | 35 ++
.../elasticsearch/utils/LogCollectConfigUtils.java | 143 ++++++++
.../elasticsearch/utils/LogCollectUtils.java | 62 ++++
.../shenyu-spring-boot-starter-plugin/pom.xml | 1 +
.../pom.xml | 31 +-
.../LoggingElasticSearchPluginConfiguration.java | 52 +++
.../src/main/resources/META-INF/spring.factories | 19 +
.../src/main/resources/META-INF/spring.provides | 18 +
34 files changed, 2441 insertions(+), 13 deletions(-)
diff --git a/db/init/mysql/schema.sql b/db/init/mysql/schema.sql
index 0b87ed9c4..332780748 100644
--- a/db/init/mysql/schema.sql
+++ b/db/init/mysql/schema.sql
@@ -593,6 +593,7 @@ INSERT INTO `plugin` VALUES ('5', 'divide', '{\"multiSelectorHandle\":\"1\",\"mu
INSERT INTO `plugin` VALUES ('6', 'dubbo', '{\"register\":\"zookeeper://localhost:2181\",\"multiSelectorHandle\":\"1\",\"threadpool\":\"cached\",\"corethreads\":0,\"threads\":2147483647,\"queues\":0}', 'Proxy', 310, 0, '2022-05-25 18:02:53', '2022-05-25 18:02:53');
INSERT INTO `plugin` VALUES ('8', 'springCloud', NULL, 'Proxy', 200, 0, '2022-05-25 18:02:53', '2022-05-25 18:02:53');
INSERT INTO `plugin` VALUES ('9', 'hystrix', NULL, 'FaultTolerance', 130, 0, '2022-05-25 18:02:53', '2022-05-25 18:02:53');
+INSERT INTO `plugin` VALUES ('32', 'loggingElasticSearch','{\"host\":\"localhost\", \"port\": \"9200\"}', 'Logging', 190, 0, '2022-06-19 22:00:00', '2022-06-19 22:00:00');
-- ----------------------------
-- Table structure for plugin_handle
@@ -779,6 +780,14 @@ INSERT INTO `plugin_handle` VALUES ('1529402613204172874', '11', 'queues', 'queu
INSERT INTO `plugin_handle` VALUES ('1529402613204172875', '11', 'threadpool', 'threadpool', 3, 3, 3, '{\"required\":\"0\",\"defaultValue\":\"default\",\"placeholder\":\"threadpool\",\"rule\":\"\"}', '2022-05-25 18:02:53', '2022-05-25 18:02:53');
INSERT INTO `plugin_handle` VALUES ('1537326008606343168', '31', 'responseContent', 'responseContent', 2, 2, 0, '{"required":"0","rule":""}', '2022-06-16 14:47:37', '2022-06-16 14:50:39');
INSERT INTO `plugin_handle` VALUES ('1537325892176658432', '31', 'httpStatusCode', 'httpStatusCode', 1, 2, 0, '{"required":"0","defaultValue":"200","rule":""}', '2022-06-16 14:47:09', '2022-06-16 14:50:39');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172876', '32', 'host', 'host', 2, 3, 1, '{\"required\":\"1\",\"defaultValue\":\"localhost\"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172877', '32', 'port', 'port', 2, 3, 2, '{\"required\":\"1\",\"defaultValue\":\"9200\"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172878', '32', 'sampleRate', 'sampleRate', 2, 3, 3, '{\"required\":\"0\",\"defaultValue\":\"1\",\"placeholder\":\"optional,0,0.01~1\"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172879', '32', 'maxResponseBody', 'maxResponseBody', 1, 3, 4, '{\"required\":\"0\",\"defaultValue\":524288}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172880', '32', 'maxRequestBody', 'maxRequestBody', 1, 3, 5, '{\"required\":\"0\",\"defaultValue\":524288}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172881', '32', 'compressAlg', 'compressAlg', 3, 3, 6, '{\"required\":\"0\",\"defaultValue\":\"none\"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172882', '32', 'index', 'index', 2, 1, 1, '{\"required\":\"0\",\"defaultValue\":\"\",\"placeholder\":\"optional\"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172883', '32', 'sampleRate', 'sampleRate', 2, 1, 2, '{\"required\":\"0\",\"defaultValue\":\"\",\"placeholder\":\"optional,0,0.01~1\"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
-- ----------------------------
diff --git a/db/init/oracle/schema.sql b/db/init/oracle/schema.sql
index 92b55c06c..50ba9348d 100644
--- a/db/init/oracle/schema.sql
+++ b/db/init/oracle/schema.sql
@@ -833,6 +833,7 @@ INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin(id)) */ INTO plugin (id, name, role
INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin(id)) */ INTO plugin (id, name, role, sort, config, enabled) VALUES ('29', 'loggingRocketMQ', 'Logging', 170,'{"topic":"shenyu-access-logging", "namesrvAddr": "localhost:9876","producerGroup":"shenyu-plugin-logging-rocketmq"}', '0');
INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin(id)) */ INTO plugin (id, name, config, role, sort, enabled) VALUES ('30', 'cache', '{"cacheType":"memory"}', 'Cache', 10, 0);
INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin(id)) */ INTO plugin (id, name, role, sort, enabled) VALUES ('31', 'mock', 'Mock', 1, 0);
+INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin(id)) */ INTO plugin (id, name, role, sort, config, enabled) VALUES ('32', 'loggingElasticSearch', '{"host":"localhost", "port": "9200"}', 'Logging', 190, 0);
@@ -1315,6 +1316,30 @@ values ('1537326008606343168', '31', 'responseContent', 'responseContent', 2, 2,
insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin_handle(plugin_id, field, type)) */ into plugin_handle (ID, PLUGIN_ID, FIELD, LABEL, DATA_TYPE, TYPE, SORT, EXT_OBJ)
values ('1537325892176658432', '31', 'httpStatusCode', 'httpStatusCode', 1, 2, 0, '{"required":"0","defaultValue":"200","rule":""}');
+insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin_handle(plugin_id, field, type)) */ into plugin_handle (ID, PLUGIN_ID, FIELD, LABEL, DATA_TYPE, TYPE, SORT, EXT_OBJ)
+values ('1518229897214468168', '32', 'host', 'host', 2, 3, 1, '{"required":"1","defaultValue":"localhost"}');
+
+insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin_handle(plugin_id, field, type)) */ into plugin_handle (ID, PLUGIN_ID, FIELD, LABEL, DATA_TYPE, TYPE, SORT, EXT_OBJ)
+values ('1518229897214468169', '32', 'port', 'port', 2, 3, 2, '{"required":"1","defaultValue":"9200"}');
+
+insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin_handle(plugin_id, field, type)) */ into plugin_handle (ID, PLUGIN_ID, FIELD, LABEL, DATA_TYPE, TYPE, SORT, EXT_OBJ)
+values ('1518229897214468170', '32', 'sampleRate', 'sampleRate', 2, 3, 4, '{"required":"0","defaultValue":"1","placeholder":"optional,0,0.01~1"}');
+
+insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin_handle(plugin_id, field, type)) */ into plugin_handle (ID, PLUGIN_ID, FIELD, LABEL, DATA_TYPE, TYPE, SORT, EXT_OBJ)
+values ('1518229897214468171', '32', 'maxResponseBody', 'maxResponseBody', 1, 3, 5, '{"required":"0","defaultValue":524288}');
+
+insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin_handle(plugin_id, field, type)) */ into plugin_handle (ID, PLUGIN_ID, FIELD, LABEL, DATA_TYPE, TYPE, SORT, EXT_OBJ)
+values ('1518229897214468172', '32', 'maxRequestBody', 'maxRequestBody', 1, 3, 6, '{"required":"0","defaultValue":524288}');
+
+insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin_handle(plugin_id, field, type)) */ into plugin_handle (ID, PLUGIN_ID, FIELD, LABEL, DATA_TYPE, TYPE, SORT, EXT_OBJ)
+values ('1518229897214468173', '32', 'compressAlg', 'compressAlg', 3, 3, 7, '{"required":"0","defaultValue":"none"}');
+
+insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin_handle(plugin_id, field, type)) */ into plugin_handle (ID, PLUGIN_ID, FIELD, LABEL, DATA_TYPE, TYPE, SORT, EXT_OBJ)
+values ('1518229897214468174', '32', 'index', 'index', 2, 1, 1, '{"required":"0","defaultValue":"","placeholder":"optional"}');
+
+insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin_handle(plugin_id, field, type)) */ into plugin_handle (ID, PLUGIN_ID, FIELD, LABEL, DATA_TYPE, TYPE, SORT, EXT_OBJ)
+values ('1518229897214468175', '32', 'sampleRate', 'sampleRate', 2, 1, 2, '{"required":"0","defaultValue":"","placeholder":"optional,0,0.01~1"}');
+
/** insert resource for resource */
diff --git a/db/init/pg/create-table.sql b/db/init/pg/create-table.sql
index a5b7ae2d2..d24fa2c73 100644
--- a/db/init/pg/create-table.sql
+++ b/db/init/pg/create-table.sql
@@ -670,6 +670,7 @@ INSERT INTO "public"."plugin" VALUES ('28', 'mqtt', '{"port": 9500,"bossGroupThr
INSERT INTO "public"."plugin" VALUES ('29', 'loggingRocketMQ', '{"topic":"shenyu-access-logging", "namesrvAddr": "localhost:9876","producerGroup":"shenyu-plugin-logging-rocketmq"}', 'Logging', 170, 0, '2022-05-25 18:08:01', '2022-05-25 18:08:01');
INSERT INTO "public"."plugin" VALUES ('30', 'cache', '{"cacheType":"memory"}', 'Cache', 10, 0, '2022-05-25 18:08:01', '2022-05-25 18:08:01');
INSERT INTO "public"."plugin" VALUES ('31', 'mock', null, 'Mock', 1, 0, '2022-06-16 14:40:35', '2022-06-16 14:40:55');
+INSERT INTO "public"."plugin" VALUES ('32', 'loggingElasticSearch','{"host":"localhost", "port": "9200"}', 'Logging', 190, 0, '2022-06-19 22:00:00', '2022-06-19 22:00:00');
-- ----------------------------
-- Table structure for plugin_handle
@@ -864,6 +865,14 @@ INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524919', '11', 'queue
INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524920', '11', 'threadpool', 'threadpool', 3, 3, 3, '{"required":"0","defaultValue":"default","placeholder":"threadpool","rule":""}', '2022-05-25 18:08:01', '2022-05-25 18:08:01');
INSERT INTO "public"."plugin_handle" VALUES ('1537326008606343168', '31', 'responseContent', 'responseContent', 2, 2, 0, '{"required":"0","rule":""}', '2022-06-16 14:47:37', '2022-06-16 14:50:39');
INSERT INTO "public"."plugin_handle" VALUES ('1537325892176658432', '31', 'httpStatusCode', 'httpStatusCode', 1, 2, 0, '{"required":"0","defaultValue":"200","rule":""}', '2022-06-16 14:47:09', '2022-06-16 14:50:39');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524921', '32', 'host', 'host', 2, 3, 2, '{"required":"1","defaultValue":"localhost"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524922', '32', 'port', 'port', 2, 3, 3, '{"required":"1","defaultValue":"9200"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524923', '32', 'sampleRate', 'sampleRate', 2, 3, 4, '{"required":"0","defaultValue":"1","placeholder":"optional,0,0.01~1"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524924', '32', 'maxResponseBody', 'maxResponseBody', 1, 3, 5, '{"required":"0","defaultValue":524288}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524925', '32', 'maxRequestBody', 'maxRequestBody', 1, 3, 6, '{"required":"0","defaultValue":524288}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524926', '32', 'compressAlg', 'compressAlg', 3, 3, 7, '{"required":"0","defaultValue":"none"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524927', '32', 'index', 'index', 2, 1, 1, '{"required":"0","defaultValue":"","placeholder":"optional"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524928', '32', 'sampleRate', 'sampleRate', 2, 1, 2, '{"required":"0","defaultValue":"","placeholder":"optional,0,0.01~1"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
-- ----------------------------
-- Table structure for resource
diff --git a/db/upgrade/2.4.3-upgrade-2.5.0-mysql.sql b/db/upgrade/2.4.3-upgrade-2.5.0-mysql.sql
index 31279985c..03efd48e7 100644
--- a/db/upgrade/2.4.3-upgrade-2.5.0-mysql.sql
+++ b/db/upgrade/2.4.3-upgrade-2.5.0-mysql.sql
@@ -71,4 +71,17 @@ INSERT INTO `resource` VALUES ('1534585531108564992', '1534585430311051264', 'se
INSERT INTO `permission` VALUES ('1534577122279825408', '1346358560427216896', '1534577121923309568', '2022-06-09 00:44:32', '2022-06-09 00:44:31');
INSERT INTO `permission` VALUES ('1534585430587875328', '1346358560427216896', '1534585430311051264', '2022-06-09 01:17:33', '2022-06-09 01:17:32');
-INSERT INTO `permission` VALUES ('1534585531389583360', '1346358560427216896', '1534585531108564992', '2022-06-09 01:17:57', '2022-06-09 01:17:56');
\ No newline at end of file
+INSERT INTO `permission` VALUES ('1534585531389583360', '1346358560427216896', '1534585531108564992', '2022-06-09 01:17:57', '2022-06-09 01:17:56');
+
+/* insert plugin for loggingElasticSearch */
+INSERT INTO `plugin` VALUES ('32', 'loggingElasticSearch','{\"host\":\"localhost\", \"port\": \"9200\"}', 'Logging', 190, 0, '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+
+/*insert plugin_handle data for plugin loggingElasticSearch*/
+INSERT INTO `plugin_handle` VALUES ('1529402613204172876', '32', 'host', 'host', 2, 3, 1, '{\"required\":\"1\",\"defaultValue\":\"localhost\"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172877', '32', 'port', 'port', 2, 3, 2, '{\"required\":\"1\",\"defaultValue\":\"9200\"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172878', '32', 'sampleRate', 'sampleRate', 2, 3, 3, '{\"required\":\"0\",\"defaultValue\":\"1\",\"placeholder\":\"optional,0,0.01~1\"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172879', '32', 'maxResponseBody', 'maxResponseBody', 1, 3, 4, '{\"required\":\"0\",\"defaultValue\":524288}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172880', '32', 'maxRequestBody', 'maxRequestBody', 1, 3, 5, '{\"required\":\"0\",\"defaultValue\":524288}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172881', '32', 'compressAlg', 'compressAlg', 3, 3, 6, '{\"required\":\"0\",\"defaultValue\":\"none\"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172882', '32', 'index', 'index', 2, 1, 1, '{\"required\":\"0\",\"defaultValue\":\"\",\"placeholder\":\"optional\"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO `plugin_handle` VALUES ('1529402613204172883', '32', 'sampleRate', 'sampleRate', 2, 1, 2, '{\"required\":\"0\",\"defaultValue\":\"\",\"placeholder\":\"optional,0,0.01~1\"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
\ No newline at end of file
diff --git a/db/upgrade/2.4.3-upgrade-2.5.0-pg.sql b/db/upgrade/2.4.3-upgrade-2.5.0-pg.sql
index 10c5cc0d6..59935fc69 100644
--- a/db/upgrade/2.4.3-upgrade-2.5.0-pg.sql
+++ b/db/upgrade/2.4.3-upgrade-2.5.0-pg.sql
@@ -81,4 +81,17 @@ INSERT INTO "public"."resource" VALUES ('1534585531108564992', '1534585430311051
INSERT INTO "public"."permission" VALUES ('1529403932886044767', '1346358560427216896', '1534577121923309568', '2022-06-09 00:44:32', '2022-06-09 00:44:31');
INSERT INTO "public"."permission" VALUES ('1529403932886044768', '1346358560427216896', '1534585430311051264', '2022-06-09 01:17:33', '2022-06-09 01:17:32');
-INSERT INTO "public"."permission" VALUES ('1529403932886044769', '1346358560427216896', '1534585531108564992', '2022-06-09 01:17:57', '2022-06-09 01:17:56');
\ No newline at end of file
+INSERT INTO "public"."permission" VALUES ('1529403932886044769', '1346358560427216896', '1534585531108564992', '2022-06-09 01:17:57', '2022-06-09 01:17:56');
+
+/* insert plugin for loggingElasticSearch */
+INSERT INTO "public"."plugin" VALUES ('32', 'loggingElasticSearch','{"host":"localhost", "port": "9200"}', 'Logging', 190, 0, '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+
+/*insert plugin_handle data for plugin loggingElasticSearch*/
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524921', '32', 'host', 'host', 2, 3, 2, '{"required":"1","defaultValue":"localhost"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524922', '32', 'port', 'port', 2, 3, 3, '{"required":"1","defaultValue":"9200"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524923', '32', 'sampleRate', 'sampleRate', 2, 3, 4, '{"required":"0","defaultValue":"1","placeholder":"optional,0,0.01~1"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524924', '32', 'maxResponseBody', 'maxResponseBody', 1, 3, 5, '{"required":"0","defaultValue":524288}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524925', '32', 'maxRequestBody', 'maxRequestBody', 1, 3, 6, '{"required":"0","defaultValue":524288}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524926', '32', 'compressAlg', 'compressAlg', 3, 3, 7, '{"required":"0","defaultValue":"none"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524927', '32', 'index', 'index', 2, 1, 1, '{"required":"0","defaultValue":"","placeholder":"optional"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
+INSERT INTO "public"."plugin_handle" VALUES ('1529403902783524928', '32', 'sampleRate', 'sampleRate', 2, 1, 2, '{"required":"0","defaultValue":"","placeholder":"optional,0,0.01~1"}', '2022-06-19 22:00:00', '2022-06-19 22:00:00');
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index b8edfc629..5d61956b2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -127,6 +127,10 @@
<grpc.version>1.33.1</grpc.version>
<rocketmq-client.version>4.9.3</rocketmq-client.version>
<lz4-java.version>1.8.0</lz4-java.version>
+ <elasticsearch-java.version>8.2.3</elasticsearch-java.version>
+ <jackson-databind.version>2.12.3</jackson-databind.version>
+ <jakarta.json-api.version>2.0.1</jakarta.json-api.version>
+ <elasticsearch-rest-client.version>8.2.3</elasticsearch-rest-client.version>
<!--maven plugin version-->
<exec-maven-plugin.version>1.6.0</exec-maven-plugin.version>
<jacoco-maven-plugin.version>0.8.7</jacoco-maven-plugin.version>
@@ -468,6 +472,18 @@
<version>${spring-cloud.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.elasticsearch.client</groupId>
+ <artifactId>elasticsearch-rest-client</artifactId>
+ <version>${elasticsearch-rest-client.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>jakarta.json</groupId>
+ <artifactId>jakarta.json-api</artifactId>
+ <version>${jakarta.json-api.version}</version>
+ </dependency>
+
</dependencies>
</dependencyManagement>
diff --git a/shenyu-admin/src/main/resources/sql-script/h2/schema.sql b/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
index 13ef6efe7..6b617a91d 100644
--- a/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
+++ b/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
@@ -380,6 +380,7 @@ INSERT IGNORE INTO `plugin` (`id`, `name`, `role`, `sort`, `config`, `enabled`)
INSERT IGNORE INTO `plugin` (`id`, `name`, `role`, `sort`, `config`, `enabled`) VALUES ('29', 'loggingRocketMQ', 'Logging', 170,'{"topic":"shenyu-access-logging", "namesrvAddr": "localhost:9876","producerGroup":"shenyu-plugin-logging-rocketmq"}', '0');
INSERT IGNORE INTO `plugin` (`id`, `name`, `config`, `role`, `sort`, `enabled`) VALUES ('30', 'cache', '{"cacheType":"memory"}', 'Cache', 10, 0);
INSERT IGNORE INTO `plugin` (`id`, `name`, `config`, `role`, `sort`, `enabled`) VALUES ('31', 'mock', null, 'Mock', 1, 0);
+INSERT IGNORE INTO `plugin` (`id`, `name`, `role`, `sort`, `config`, `enabled`) VALUES ('32', 'loggingElasticSearch', 'Logging', 190,'{"host":"localhost", "port": "9200"}', '0');
/*insert plugin_handle data for sentinel*/
INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1529402613195784246', '10', 'flowRuleGrade', 'flowRuleGrade', 3, 2, 8, '{"required":"1","defaultValue":"1","rule":""}');
@@ -543,8 +544,16 @@ INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,
INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1529402613204172873', '11', 'threads', 'threads', 1, 3, 5, '{"required":"0","defaultValue":"2147483647","placeholder":"threads","rule":""}');
INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1529402613204172874', '11', 'queues', 'queues', 1, 3, 6, '{"required":"0","defaultValue":"0","placeholder":"queues","rule":""}');
INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1529402613204172875', '11', 'threadpool', 'threadpool', 3, 3, 3, '{"required":"0","defaultValue":"default","placeholder":"threadpool","rule":""}');
-INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1537326008606343168', '31', 'responseContent', 'responseContent', 2, 2, 0, '{"required":"0","rule":""}');
+INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1537326008606343168', '31', 'responseContent', 'responseContent', 2, 2, 0, '{"required":"0","rule":""}');
INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1537325892176658432', '31', 'httpStatusCode', 'httpStatusCode', 1, 2, 0, '{"required":"0","defaultValue":"200","rule":""}');
+INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1529402613204172876', '32', 'host', 'host', 2, 3, 1, '{"required":"1","defaultValue":"localhost"}');
+INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1529402613204172877', '32', 'port', 'port', 2, 3, 2, '{"required":"1","defaultValue":"9200"}');
+INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1529402613204172878', '32', 'sampleRate', 'sampleRate', 2, 3, 3, '{"required":"0","defaultValue":"1","placeholder":"optional,0,0.01~1"}');
+INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1529402613204172879', '32', 'maxResponseBody', 'maxResponseBody', 1, 3, 4, '{"required":"0","defaultValue":524288}');
+INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1529402613204172880', '32', 'maxRequestBody', 'maxRequestBody', 1, 3, 5, '{"required":"0","defaultValue":524288}');
+INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1529402613204172881', '32', 'compressAlg', 'compressAlg', 3, 3, 6, '{"required":"0","defaultValue":"none"}');
+INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1529402613204172882', '32', 'index', 'index', 2, 1, 1, '{"required":"0","defaultValue":"","placeholder":"optional"}');
+INSERT IGNORE INTO plugin_handle (`id`, `plugin_id`,`field`,`label`,`data_type`,`type`,`sort`,`ext_obj`) VALUES ('1529402613204172883', '32', 'sampleRate', 'sampleRate', 2, 1, 2, '{"required":"0","defaultValue":"","placeholder":"optional,0,0.01~1"}');
diff --git a/shenyu-bootstrap/pom.xml b/shenyu-bootstrap/pom.xml
index 7f626dbeb..60cfd3aaa 100644
--- a/shenyu-bootstrap/pom.xml
+++ b/shenyu-bootstrap/pom.xml
@@ -469,6 +469,14 @@
<version>${project.version}</version>
</dependency>
<!--shenyu logging-rocketmq plugin end-->
+
+ <!--shenyu logging-elasticsearch plugin start-->
+ <dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-spring-boot-starter-plugin-logging-elasticsearch</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!--shenyu logging-elasticsearch plugin end-->
</dependencies>
<profiles>
<profile>
diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/enums/PluginEnum.java b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/PluginEnum.java
index 9548c306c..3ec0276c7 100644
--- a/shenyu-common/src/main/java/org/apache/shenyu/common/enums/PluginEnum.java
+++ b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/PluginEnum.java
@@ -134,6 +134,11 @@ public enum PluginEnum {
* Logging RocketMQ plugin enum.
*/
LOGGING_ROCKETMQ(170, 0, "loggingRocketMQ"),
+
+ /**
+ * Logging ElasticSearch plugin enum.
+ */
+ LOGGING_ElasticSearch(190, 0, "loggingElasticSearch"),
/**
* Divide plugin enum.
diff --git a/shenyu-plugin/shenyu-plugin-logging/pom.xml b/shenyu-plugin/shenyu-plugin-logging/pom.xml
index dddc7a374..beb8864ef 100644
--- a/shenyu-plugin/shenyu-plugin-logging/pom.xml
+++ b/shenyu-plugin/shenyu-plugin-logging/pom.xml
@@ -31,5 +31,6 @@
<modules>
<module>shenyu-plugin-logging-console</module>
<module>shenyu-plugin-logging-rocketmq</module>
+ <module>shenyu-plugin-logging-elasticsearch</module>
</modules>
</project>
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/pom.xml b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/pom.xml
new file mode 100644
index 000000000..23897f460
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-plugin-logging</artifactId>
+ <version>2.5.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>shenyu-plugin-logging-elasticsearch</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-plugin-base</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.lz4</groupId>
+ <artifactId>lz4-java</artifactId>
+ <version>${lz4-java.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.projectreactor</groupId>
+ <artifactId>reactor-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>co.elastic.clients</groupId>
+ <artifactId>elasticsearch-java</artifactId>
+ <version>${elasticsearch-java.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>${jackson-databind.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>jakarta.json</groupId>
+ <artifactId>jakarta.json-api</artifactId>
+ <version>${jakarta.json-api.version}</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/AbstractLogCollector.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/AbstractLogCollector.java
new file mode 100644
index 000000000..4aa98db44
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/AbstractLogCollector.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch;
+
+import org.apache.shenyu.common.utils.ThreadUtils;
+import org.apache.shenyu.plugin.logging.elasticsearch.entity.ShenyuRequestLog;
+import org.apache.shenyu.plugin.logging.elasticsearch.utils.LogCollectConfigUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * abstract log collector,Contains common methods.
+ */
+public abstract class AbstractLogCollector implements LogCollector {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractLogCollector.class);
+
+ private int bufferSize;
+
+ private BlockingQueue<ShenyuRequestLog> bufferQueue;
+
+ private long lastPushTime;
+
+ private final AtomicBoolean started = new AtomicBoolean(true);
+
+ @Override
+ public void start() {
+ bufferSize = LogCollectConfigUtils.getGlobalLogConfig().getBufferQueueSize();
+ bufferQueue = new LinkedBlockingDeque<>(bufferSize);
+ ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
+ started.set(true);
+ threadExecutor.execute(this::consume);
+ }
+
+ @Override
+ public void collect(final ShenyuRequestLog log) {
+ if (Objects.isNull(log) || Objects.isNull(getLogConsumeClient())) {
+ return;
+ }
+ if (bufferQueue.size() < bufferSize) {
+ bufferQueue.add(log);
+ }
+ }
+
+ /**
+ * batch and async consume.
+ */
+ private void consume() {
+ while (started.get()) {
+ int diffTimeMSForPush = 100;
+ try {
+ List<ShenyuRequestLog> logs = new ArrayList<>();
+ int size = bufferQueue.size();
+ long time = System.currentTimeMillis();
+ long timeDiffMs = time - lastPushTime;
+ int batchSize = 100;
+ if (size >= batchSize || timeDiffMs > diffTimeMSForPush) {
+ bufferQueue.drainTo(logs, batchSize);
+ LogConsumeClient logCollectClient = getLogConsumeClient();
+ if (logCollectClient != null) {
+ logCollectClient.consume(logs);
+ }
+ lastPushTime = time;
+ } else {
+ ThreadUtils.sleep(TimeUnit.MILLISECONDS, diffTimeMSForPush);
+ }
+ } catch (Exception e) {
+ LOG.error("DefaultLogCollector collect log error", e);
+ ThreadUtils.sleep(TimeUnit.MILLISECONDS, diffTimeMSForPush);
+ }
+ }
+ }
+
+ /**
+ * get log consume client.
+ *
+ * @return log consume client
+ */
+ protected abstract LogConsumeClient getLogConsumeClient();
+
+ @Override
+ public void close() throws Exception {
+ started.set(false);
+ LogConsumeClient logCollectClient = getLogConsumeClient();
+ if (logCollectClient != null) {
+ logCollectClient.close();
+ }
+ }
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/DefaultLogCollector.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/DefaultLogCollector.java
new file mode 100644
index 000000000..29d4ae72b
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/DefaultLogCollector.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch;
+
+import org.apache.shenyu.plugin.logging.elasticsearch.handler.LoggingElasticSearchPluginDataHandler;
+
+/**
+ * default log collector,depend a LogConsumeClient for consume logs.
+ */
+public class DefaultLogCollector extends AbstractLogCollector {
+
+ private static final LogCollector INSTANCE = new DefaultLogCollector();
+
+ /**
+ * get LogCollector instance.
+ *
+ * @return LogCollector instance
+ */
+ public static LogCollector getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ protected LogConsumeClient getLogConsumeClient() {
+ return LoggingElasticSearchPluginDataHandler.getElasticSearchLogCollectClient();
+ }
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/LogCollector.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/LogCollector.java
new file mode 100644
index 000000000..04d2595c6
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/LogCollector.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch;
+
+import org.apache.shenyu.plugin.logging.elasticsearch.entity.ShenyuRequestLog;
+
+/**
+ * Collect logs and put into buffer queue.
+ */
+public interface LogCollector extends AutoCloseable {
+
+ /**
+ * start log collector.
+ */
+ void start();
+
+ /**
+ * collect log.
+ *
+ * @param log access log
+ */
+ void collect(ShenyuRequestLog log);
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/LogConsumeClient.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/LogConsumeClient.java
new file mode 100644
index 000000000..0d3810f67
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/LogConsumeClient.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch;
+
+import org.apache.shenyu.plugin.logging.elasticsearch.entity.ShenyuRequestLog;
+
+import java.util.List;
+
+/**
+ * Used to collect logs, which can be stored in remote or local files or databases, or others.
+ */
+public interface LogConsumeClient extends AutoCloseable {
+
+ /**
+ * collect logs.
+ *
+ * @param logs list of log
+ * @throws Exception produce exception
+ */
+ void consume(List<ShenyuRequestLog> logs) throws Exception;
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/LoggingElasticSearchPlugin.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/LoggingElasticSearchPlugin.java
new file mode 100644
index 000000000..0c861540e
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/LoggingElasticSearchPlugin.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch;
+
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.base.AbstractShenyuPlugin;
+import org.apache.shenyu.plugin.base.utils.HostAddressUtils;
+import org.apache.shenyu.plugin.logging.elasticsearch.body.LoggingElasticSearchServerHttpRequest;
+import org.apache.shenyu.plugin.logging.elasticsearch.body.LoggingElasticSearchServerResponse;
+import org.apache.shenyu.plugin.logging.elasticsearch.entity.ShenyuRequestLog;
+import org.apache.shenyu.plugin.logging.elasticsearch.utils.LogCollectConfigUtils;
+import org.apache.shenyu.plugin.logging.elasticsearch.utils.LogCollectUtils;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import static org.apache.shenyu.common.enums.PluginEnum.LOGGING_ElasticSearch;
+
+/**
+ * Integrated elasticsearch collect log.
+ */
+public class LoggingElasticSearchPlugin extends AbstractShenyuPlugin {
+
+ private static final String USER_AGENT = "User-Agent";
+
+ private static final String HOST = "Host";
+
+ @Override
+ protected Mono<Void> doExecute(final ServerWebExchange exchange, final ShenyuPluginChain chain,
+ final SelectorData selector, final RuleData rule) {
+ ServerHttpRequest request = exchange.getRequest();
+ // control sampling
+ if (!LogCollectConfigUtils.isSampled(exchange.getRequest())) {
+ return chain.execute(exchange);
+ }
+
+ ShenyuRequestLog requestInfo = new ShenyuRequestLog();
+ requestInfo.setRequestUri(request.getURI().toString());
+ requestInfo.setMethod(request.getMethodValue());
+ requestInfo.setRequestHeader(LogCollectUtils.getHeaders(request.getHeaders()));
+ requestInfo.setQueryParams(request.getURI().getQuery());
+ requestInfo.setClientIp(HostAddressUtils.acquireIp(exchange));
+ requestInfo.setUserAgent(request.getHeaders().getFirst(USER_AGENT));
+ requestInfo.setHost(request.getHeaders().getFirst(HOST));
+ requestInfo.setPath(request.getURI().getPath());
+
+ LoggingElasticSearchServerHttpRequest loggingElasticSearchServerHttpRequest = new LoggingElasticSearchServerHttpRequest(request, requestInfo);
+ LoggingElasticSearchServerResponse loggingElasticSearchServerResponse = new LoggingElasticSearchServerResponse(exchange.getResponse(),
+ requestInfo, DefaultLogCollector.getInstance());
+ ServerWebExchange webExchange = exchange.mutate().request(loggingElasticSearchServerHttpRequest)
+ .response(loggingElasticSearchServerResponse).build();
+ loggingElasticSearchServerResponse.setExchange(webExchange);
+
+ return chain.execute(webExchange).doOnError(loggingElasticSearchServerResponse::logError);
+ }
+
+ /**
+ * get plugin order.
+ *
+ * @return order
+ */
+ @Override
+ public int getOrder() {
+ return LOGGING_ElasticSearch.getCode();
+ }
+
+ /**
+ * get plugin name.
+ *
+ * @return plugin name
+ */
+ @Override
+ public String named() {
+ return LOGGING_ElasticSearch.getName();
+ }
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/body/BodyWriter.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/body/BodyWriter.java
new file mode 100644
index 000000000..8ff7ae73e
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/body/BodyWriter.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch.body;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.WritableByteChannel;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * bodyWriter is used to read Body.
+ */
+public class BodyWriter {
+
+ private static final Logger LOG = LoggerFactory.getLogger(BodyWriter.class);
+
+ private final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+
+ private final WritableByteChannel channel = Channels.newChannel(stream);
+
+ private final AtomicBoolean isClosed = new AtomicBoolean(false);
+
+
+ /**
+ * write ByteBuffer.
+ *
+ * @param buffer byte buffer
+ */
+ public void write(final ByteBuffer buffer) {
+ if (!isClosed.get()) {
+ try {
+ channel.write(buffer);
+ } catch (IOException e) {
+ isClosed.compareAndSet(false, true);
+ LOG.error("write buffer Failed.", e);
+ }
+ }
+ }
+
+ /**
+ * get stream size.
+ *
+ * @return size of stream
+ */
+ public int size() {
+ return stream.size();
+ }
+
+ /**
+ * output stream value.
+ *
+ * @return string of stream
+ */
+ public String output() {
+ if (isEmpty()) {
+ return "";
+ }
+ try {
+ isClosed.compareAndSet(false, true);
+ return new String(stream.toByteArray(), StandardCharsets.UTF_8);
+ } catch (Exception e) {
+ LOG.error("Write failed: ", e);
+ return "Write failed: " + e.getMessage();
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ LOG.error("Close stream error: ", e);
+ }
+ try {
+ channel.close();
+ } catch (IOException e) {
+ LOG.error("Close channel error: ", e);
+ }
+ }
+ }
+
+ /**
+ * judge stream is empty.
+ *
+ * @return true: stream is empty
+ */
+ boolean isEmpty() {
+ return stream.size() == 0;
+ }
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/body/LoggingElasticSearchServerHttpRequest.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/body/LoggingElasticSearchServerHttpRequest.java
new file mode 100644
index 000000000..8dd57407a
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/body/LoggingElasticSearchServerHttpRequest.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch.body;
+
+import org.apache.shenyu.plugin.logging.elasticsearch.entity.ShenyuRequestLog;
+
+import org.apache.shenyu.plugin.logging.elasticsearch.utils.LogCollectConfigUtils;
+import org.apache.shenyu.plugin.logging.elasticsearch.utils.LogCollectUtils;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
+import reactor.core.publisher.Flux;
+import reactor.util.annotation.NonNull;
+
+/**
+ * decorate ServerHttpRequest for read org.apache.shenyu.plugin.logging.body.
+ */
+public class LoggingElasticSearchServerHttpRequest extends ServerHttpRequestDecorator {
+
+ private final ShenyuRequestLog logInfo;
+
+ public LoggingElasticSearchServerHttpRequest(final ServerHttpRequest delegate, final ShenyuRequestLog logInfo) {
+ super(delegate);
+ this.logInfo = logInfo;
+ }
+
+ /**
+ * get request org.apache.shenyu.plugin.logging.body.
+ *
+ * @return Flux
+ */
+ @Override
+ @NonNull
+ public Flux<DataBuffer> getBody() {
+ BodyWriter writer = new BodyWriter();
+ return super.getBody().doOnNext(dataBuffer -> {
+ if (LogCollectUtils.isNotBinaryType(getHeaders())) {
+ writer.write(dataBuffer.asByteBuffer().asReadOnlyBuffer());
+ }
+ }).doFinally(signal -> {
+ int size = writer.size();
+ String body = writer.output();
+ boolean requestBodyTooLarge = LogCollectConfigUtils.isRequestBodyTooLarge(size);
+ if (size == 0 || requestBodyTooLarge) {
+ return;
+ }
+ logInfo.setRequestBody(body);
+ });
+ }
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/body/LoggingElasticSearchServerResponse.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/body/LoggingElasticSearchServerResponse.java
new file mode 100644
index 000000000..89fed618c
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/body/LoggingElasticSearchServerResponse.java
@@ -0,0 +1,254 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch.body;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.common.constant.Constants;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.apache.shenyu.common.utils.DateUtils;
+import org.apache.shenyu.plugin.api.context.ShenyuContext;
+import org.apache.shenyu.plugin.api.result.ShenyuResult;
+import org.apache.shenyu.plugin.api.result.ShenyuResultWrap;
+import org.apache.shenyu.plugin.logging.elasticsearch.entity.ShenyuRequestLog;
+import org.apache.shenyu.plugin.logging.elasticsearch.DefaultLogCollector;
+import org.apache.shenyu.plugin.logging.elasticsearch.LogCollector;
+import org.apache.shenyu.plugin.logging.elasticsearch.constant.LoggingConstant;
+import org.apache.shenyu.plugin.logging.elasticsearch.utils.LogCollectConfigUtils;
+import org.apache.shenyu.plugin.logging.elasticsearch.utils.LogCollectUtils;
+import org.reactivestreams.Publisher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
+import org.springframework.web.server.ResponseStatusException;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.util.annotation.NonNull;
+
+import java.net.URI;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * decorate ServerHttpResponse for read org.apache.shenyu.plugin.logging.body.
+ */
+public class LoggingElasticSearchServerResponse extends ServerHttpResponseDecorator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(LoggingElasticSearchServerResponse.class);
+
+ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
+
+ private final ShenyuRequestLog logInfo;
+
+ private ServerWebExchange exchange;
+
+ private final LogCollector logCollector;
+
+ /**
+ * Constructor LoggingServerHttpResponse.
+ *
+ * @param delegate delegate ServerHttpResponse
+ * @param logInfo access log
+ * @param logCollector LogCollector instance
+ */
+ public LoggingElasticSearchServerResponse(final ServerHttpResponse delegate, final ShenyuRequestLog logInfo,
+ final LogCollector logCollector) {
+ super(delegate);
+ this.logInfo = logInfo;
+ this.logCollector = logCollector;
+ }
+
+ /**
+ * set relevant ServerWebExchange.
+ *
+ * @param exchange ServerWebExchange
+ */
+ public void setExchange(final ServerWebExchange exchange) {
+ this.exchange = exchange;
+ }
+
+ /**
+ * write with a publisher.
+ *
+ * @param body response org.apache.shenyu.plugin.logging.body
+ * @return Mono
+ */
+ @Override
+ @NonNull
+ public Mono<Void> writeWith(@NonNull final Publisher<? extends DataBuffer> body) {
+ return super.writeWith(appendResponse(body));
+ }
+
+ /**
+ * append response.
+ *
+ * @param body publisher
+ * @return wrap Flux
+ */
+ @NonNull
+ private Flux<? extends DataBuffer> appendResponse(final Publisher<? extends DataBuffer> body) {
+ ShenyuContext shenyuContext = exchange.getAttribute(Constants.CONTEXT);
+ assert shenyuContext != null;
+ if (getStatusCode() != null) {
+ logInfo.setStatus(getStatusCode().value());
+ }
+ logInfo.setResponseHeader(LogCollectUtils.getHeaders(getHeaders()));
+ BodyWriter writer = new BodyWriter();
+ logInfo.setTraceId(getTraceId());
+ return Flux.from(body).doOnNext(buffer -> {
+ if (LogCollectUtils.isNotBinaryType(getHeaders())) {
+ writer.write(buffer.asByteBuffer().asReadOnlyBuffer());
+ }
+ }).doFinally(signal -> logResponse(shenyuContext, writer));
+ }
+
+ /**
+ * record response log.
+ *
+ * @param shenyuContext request context
+ * @param writer bodyWriter
+ */
+ private void logResponse(final ShenyuContext shenyuContext, final BodyWriter writer) {
+ if (StringUtils.isNotBlank(getHeaders().getFirst(HttpHeaders.CONTENT_LENGTH))) {
+ String size = StringUtils.defaultIfEmpty(getHeaders().getFirst(HttpHeaders.CONTENT_LENGTH), "0");
+ logInfo.setResponseContentLength(Integer.parseInt(size));
+ } else {
+ logInfo.setResponseContentLength(writer.size());
+ }
+ logInfo.setTimeLocal(shenyuContext.getStartDateTime().format(DATE_TIME_FORMATTER));
+ logInfo.setModule(shenyuContext.getModule());
+ long costTime = DateUtils.acquireMillisBetween(shenyuContext.getStartDateTime(), LocalDateTime.now());
+ logInfo.setUpstreamResponseTime(costTime);
+ logInfo.setMethod(shenyuContext.getMethod());
+ logInfo.setRpcType(shenyuContext.getRpcType());
+ if (StringUtils.isNotBlank(shenyuContext.getRpcType())) {
+ logInfo.setUpstreamIp(getUpstreamIp());
+ }
+ int size = writer.size();
+ String body = writer.output();
+ if (size > 0 && !LogCollectConfigUtils.isResponseBodyTooLarge(size)) {
+ logInfo.setResponseBody(body);
+ }
+ // collect log
+ if (logCollector != null) {
+ logCollector.collect(logInfo);
+ }
+ }
+
+ /**
+ * get upstream ip.
+ *
+ * @return upstream ip
+ */
+ private String getUpstreamIp() {
+ ShenyuContext shenyuContext = exchange.getAttribute(Constants.CONTEXT);
+ assert shenyuContext != null;
+ if (RpcTypeEnum.HTTP.getName().equals(shenyuContext.getRpcType())) {
+ URI uri = exchange.getAttribute(Constants.HTTP_URI);
+ if (uri != null) {
+ return uri.getHost();
+ } else {
+ return getUpstreamIpFromHttpDomain();
+ }
+ } else {
+ String domain = (String) exchange.getAttributes().get(Constants.HTTP_DOMAIN);
+ if (StringUtils.isNotBlank(domain)) {
+ return getUpstreamIpFromHttpDomain();
+ }
+ // The current context is difficult to obtain the upstream IP of grpc and Dubbo. need change plugin code.
+ }
+ return "";
+ }
+
+ /**
+ * Encourage developers to provide plugins to upstream like SkyWalking, ZipKin and OpenTelemetry
+ * to implement the tracing features. These plug-ins can set a traceId to the context.
+ *
+ * @return traceId
+ */
+ private String getTraceId() {
+ return (String) exchange.getAttributes().get(LoggingConstant.SHENYU_AGENT_TRACE_ID);
+ }
+
+ /**
+ * collect access error.
+ *
+ * @param throwable Exception occurred。
+ */
+ public void logError(final Throwable throwable) {
+ HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
+ if (throwable instanceof ResponseStatusException) {
+ httpStatus = ((ResponseStatusException) throwable).getStatus();
+ }
+ logInfo.setStatus(httpStatus.value());
+ logInfo.setTraceId(getTraceId());
+ // Do not collect stack
+ Object result = ShenyuResultWrap.error(exchange, httpStatus.value(),
+ httpStatus.getReasonPhrase(), throwable.getMessage());
+ final ShenyuResult<?> shenyuResult = ShenyuResultWrap.shenyuResult();
+ Object resultData = shenyuResult.format(exchange, result);
+ final Object responseData = shenyuResult.result(exchange, resultData);
+ assert null != responseData;
+ final byte[] bytes = (responseData instanceof byte[])
+ ? (byte[]) responseData
+ : responseData.toString().getBytes(StandardCharsets.UTF_8);
+ logInfo.setResponseContentLength(bytes.length);
+ ShenyuContext shenyuContext = exchange.getAttribute(Constants.CONTEXT);
+ assert shenyuContext != null;
+ logInfo.setTimeLocal(shenyuContext.getStartDateTime().format(DATE_TIME_FORMATTER));
+ logInfo.setModule(shenyuContext.getModule());
+ long costTime = DateUtils.acquireMillisBetween(shenyuContext.getStartDateTime(), LocalDateTime.now());
+ logInfo.setUpstreamResponseTime(costTime);
+ logInfo.setResponseHeader(LogCollectUtils.getHeaders(exchange.getResponse().getHeaders()));
+ logInfo.setRpcType(shenyuContext.getRpcType());
+ logInfo.setMethod(shenyuContext.getMethod());
+ if (StringUtils.isNotBlank(shenyuContext.getRpcType())) {
+ logInfo.setUpstreamIp(getUpstreamIp());
+ }
+
+ int size = bytes.length;
+ String body = new String(bytes, StandardCharsets.UTF_8);
+ if (size > 0 && !LogCollectConfigUtils.isResponseBodyTooLarge(size)) {
+ logInfo.setResponseBody(body);
+ }
+ // collect log
+ if (DefaultLogCollector.getInstance() != null) {
+ DefaultLogCollector.getInstance().collect(logInfo);
+ }
+ }
+
+ private String getUpstreamIpFromHttpDomain() {
+ String domain = (String) exchange.getAttributes().get(Constants.HTTP_DOMAIN);
+ try {
+ if (StringUtils.isNotBlank(domain)) {
+ URL url = new URL(domain);
+ return url.getHost();
+ }
+ } catch (Exception e) {
+ LOG.error("get upstream ip error");
+ }
+ return "";
+ }
+
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/config/LogCollectConfig.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/config/LogCollectConfig.java
new file mode 100644
index 000000000..96d064955
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/config/LogCollectConfig.java
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch.config;
+
+/**
+ * log collect config, include elasticsearch config.
+ * Host and port must be included, and others are optional.
+ * We should operate the configuration through admin instead of the configuration file.
+ */
+public class LogCollectConfig {
+
+ private GlobalLogConfig globalLogConfig;
+
+ /**
+ * get global log config.
+ *
+ * @return global log config
+ */
+ public GlobalLogConfig getGlobalLogConfig() {
+ return globalLogConfig;
+ }
+
+ /**
+ * set global log config.
+ *
+ * @param globalLogConfig global log config.
+ */
+ public void setGlobalLogConfig(final GlobalLogConfig globalLogConfig) {
+ this.globalLogConfig = globalLogConfig;
+ }
+
+ /**
+ * global log config.
+ */
+ public static class GlobalLogConfig {
+
+ private String host;
+
+ private String port;
+
+ private String sampleRate = "1";
+
+ private String compressAlg;
+
+ /**
+ * default 512KB.
+ */
+ private int maxResponseBody = 524288;
+
+ /**
+ * default 512kb.
+ */
+ private int maxRequestBody = 524288;
+
+ private int bufferQueueSize = 50000;
+
+ /**
+ * get sample rate.
+ *
+ * @return sample
+ */
+ public String getSampleRate() {
+ return sampleRate;
+ }
+
+ /**
+ * set sample rate.
+ *
+ * @param sampleRate rate
+ */
+ public void setSampleRate(final String sampleRate) {
+ this.sampleRate = sampleRate;
+ }
+
+ /**
+ * whether compress.
+ *
+ * @return compress or not
+ */
+ public String getCompressAlg() {
+ return compressAlg;
+ }
+
+ /**
+ * set compress.
+ *
+ * @param compressAlg compress alg.
+ */
+ public void setCompressAlg(final String compressAlg) {
+ this.compressAlg = compressAlg;
+ }
+
+ /**
+ * get max response org.apache.shenyu.plugin.logging.body.
+ *
+ * @return get max response org.apache.shenyu.plugin.logging.body
+ */
+ public int getMaxResponseBody() {
+ return maxResponseBody;
+ }
+
+ /**
+ * set max response org.apache.shenyu.plugin.logging.body.
+ *
+ * @param maxResponseBody max response org.apache.shenyu.plugin.logging.body
+ */
+ public void setMaxResponseBody(final int maxResponseBody) {
+ this.maxResponseBody = maxResponseBody;
+ }
+
+ /**
+ * get max request org.apache.shenyu.plugin.logging.body.
+ *
+ * @return max request org.apache.shenyu.plugin.logging.body
+ */
+ public int getMaxRequestBody() {
+ return maxRequestBody;
+ }
+
+ /**
+ * set max request org.apache.shenyu.plugin.logging.body.
+ *
+ * @param maxRequestBody max request org.apache.shenyu.plugin.logging.body
+ */
+ public void setMaxRequestBody(final int maxRequestBody) {
+ this.maxRequestBody = maxRequestBody;
+ }
+
+ /**
+ * get buffer queue size.
+ *
+ * @return buffer queue size
+ */
+ public int getBufferQueueSize() {
+ return bufferQueueSize;
+ }
+
+ /**
+ * set buffer queue size.
+ *
+ * @param bufferQueueSize buffer queue size
+ */
+ public void setBufferQueueSize(final int bufferQueueSize) {
+ this.bufferQueueSize = bufferQueueSize;
+ }
+
+ /**
+ * get host.
+ *
+ * @return host
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * set host.
+ *
+ * @param host host name
+ */
+ public void setHost(final String host) {
+ this.host = host;
+ }
+
+ /**
+ * get port.
+ *
+ * @return port
+ */
+ public String getPort() {
+ return port;
+ }
+
+ /**
+ * set port.
+ *
+ * @param port port name
+ */
+ public void setPort(final String port) {
+ this.port = port;
+ }
+
+ }
+
+ /**
+ * api log config.
+ */
+ public static class LogApiConfig {
+
+ private String index;
+
+ /**
+ * 0 means never sample, 1 means always sample. Minimum probability is 0.01, or 1% of logging
+ */
+ private String sampleRate;
+
+ /**
+ * get sample rate.
+ *
+ * @return sample rate
+ */
+ public String getSampleRate() {
+ return sampleRate;
+ }
+
+ /**
+ * set sample rate.
+ *
+ * @param sampleRate sample rate
+ */
+ public void setSampleRate(final String sampleRate) {
+ this.sampleRate = sampleRate;
+ }
+
+ /**
+ * get index name.
+ *
+ * @return index name
+ */
+ public String getIndex() {
+ return index;
+ }
+
+ /**
+ * set index name.
+ *
+ * @param index index name
+ */
+ public void setIndex(final String index) {
+ this.index = index;
+ }
+
+ }
+
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/constant/LoggingConstant.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/constant/LoggingConstant.java
new file mode 100644
index 000000000..80278b655
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/constant/LoggingConstant.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch.constant;
+
+/**
+ * Some log related property constants.
+ */
+public class LoggingConstant {
+
+ public static final String INDEX = "shenyu-access-logging";
+
+ public static final String HOST = "host";
+
+ public static final String PORT = "port";
+
+ public static final String SHENYU_AGENT_TRACE_ID = "shenyu-agent-trace-id";
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/elasticsearch/ElasticSearchLogCollectClient.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/elasticsearch/ElasticSearchLogCollectClient.java
new file mode 100644
index 000000000..357097e7a
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/elasticsearch/ElasticSearchLogCollectClient.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch.elasticsearch;
+
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch.core.BulkResponse;
+import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
+import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
+import co.elastic.clients.json.jackson.JacksonJsonpMapper;
+import co.elastic.clients.transport.ElasticsearchTransport;
+import co.elastic.clients.transport.endpoints.BooleanResponse;
+import co.elastic.clients.transport.rest_client.RestClientTransport;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.http.HttpHost;
+import org.apache.shenyu.plugin.logging.elasticsearch.LogConsumeClient;
+import org.apache.shenyu.plugin.logging.elasticsearch.constant.LoggingConstant;
+import org.apache.shenyu.plugin.logging.elasticsearch.entity.ShenyuRequestLog;
+import org.elasticsearch.client.RestClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * queue-based logging collector.
+ */
+public class ElasticSearchLogCollectClient implements LogConsumeClient {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ElasticSearchLogCollectClient.class);
+
+ private RestClient restClient;
+
+ private ElasticsearchTransport transport;
+
+ private ElasticsearchClient client;
+
+ private final AtomicBoolean isStarted = new AtomicBoolean(false);
+
+ /**
+ * init elasticsearch client.
+ *
+ * @param props elasticsearch client properties
+ */
+ public void initClient(final Properties props) {
+ restClient = RestClient
+ .builder(new HttpHost(props.getProperty(LoggingConstant.HOST), Integer.parseInt(props.getProperty(LoggingConstant.PORT))))
+ .build();
+ transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
+ client = new ElasticsearchClient(transport);
+ LOG.info("init ElasticSearchLogCollectClient success");
+ isStarted.set(true);
+ Runtime.getRuntime().addShutdownHook(new Thread(this::close));
+ }
+
+ @Override
+ public void consume(final List<ShenyuRequestLog> logs) throws Exception {
+ if (!existsIndex(LoggingConstant.INDEX)) {
+ createIndex(LoggingConstant.INDEX);
+ }
+ if (CollectionUtils.isEmpty(logs) || !isStarted.get()) {
+ return;
+ }
+ List<BulkOperation> bulkOperations = new ArrayList<>();
+ logs.forEach(log -> {
+ try {
+ bulkOperations.add(new BulkOperation.Builder().create(d -> d.document(log).index(LoggingConstant.INDEX)).build());
+ } catch (Exception e) {
+ LOG.error("elasticsearch store logs error", e);
+ }
+ });
+ BulkResponse response = client.bulk(e -> e.index(LoggingConstant.INDEX).operations(bulkOperations));
+ }
+
+ /**
+ * determine whether the index already exists.
+ *
+ * @param indexName index name
+ * @return true or false
+ */
+ public boolean existsIndex(final String indexName) {
+ try {
+ BooleanResponse existsResponse = client.indices().exists(c -> c.index(indexName));
+ return existsResponse.value();
+ } catch (Exception e) {
+ LOG.error("fail to check the index exists");
+ }
+ return true;
+ }
+
+ /**
+ * create elasticsearch client.
+ *
+ * @param indexName index name
+ * @return true or false
+ */
+ public boolean createIndex(final String indexName) {
+ try {
+ CreateIndexResponse createIndexResponse = client.indices().create(c -> c.index(indexName));
+ return createIndexResponse.acknowledged();
+ } catch (IOException e) {
+ LOG.error("create index error");
+ }
+ return true;
+ }
+
+ /**
+ * close client.
+ */
+ @Override
+ public void close() {
+ if (restClient != null && isStarted.get()) {
+ try {
+ transport.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ try {
+ restClient.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ isStarted.set(false);
+ }
+ }
+
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/entity/LZ4CompressData.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/entity/LZ4CompressData.java
new file mode 100644
index 000000000..d5be64ac4
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/entity/LZ4CompressData.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch.entity;
+
+/**
+ * Lz4 compressed data.
+ */
+public class LZ4CompressData {
+
+ private int length;
+
+ private byte[] compressedData;
+
+ public LZ4CompressData(final int length, final byte[] compressedData) {
+ this.length = length;
+ this.compressedData = compressedData;
+ }
+
+ /**
+ * get the exact size of the original input.
+ *
+ * @return the exact size of the original input
+ */
+ public int getLength() {
+ return length;
+ }
+
+ /**
+ * set the exact size of the original input.
+ *
+ * @param length the exact size of the original input.
+ */
+ public void setLength(final int length) {
+ this.length = length;
+ }
+
+ /**
+ * get the compressed data.
+ *
+ * @return the compressed data
+ */
+ public byte[] getCompressedData() {
+ return compressedData;
+ }
+
+ /**
+ * set the compressed data.
+ *
+ * @param compressedData the compressed data
+ */
+ public void setCompressedData(final byte[] compressedData) {
+ this.compressedData = compressedData;
+ }
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/entity/ShenyuRequestLog.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/entity/ShenyuRequestLog.java
new file mode 100644
index 000000000..821244345
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/entity/ShenyuRequestLog.java
@@ -0,0 +1,407 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch.entity;
+
+/**
+ * shenyu gateway access log.
+ */
+public class ShenyuRequestLog {
+
+ private String clientIp;
+
+ private String timeLocal;
+
+ private String method;
+
+ private String requestHeader;
+
+ private String responseHeader;
+
+ private String queryParams;
+
+ private String requestBody;
+
+ private String requestUri;
+
+ private String responseBody;
+
+ private int responseContentLength;
+
+ private String rpcType;
+
+ private int status;
+
+ private String upstreamIp;
+
+ private long upstreamResponseTime;
+
+ private String userAgent;
+
+ private String host;
+
+ private String module;
+
+ private String traceId;
+
+ /**
+ * path.
+ */
+ private String path;
+
+ /**
+ * get module.
+ *
+ * @return module
+ */
+ public String getModule() {
+ return module;
+ }
+
+ /**
+ * set module.
+ *
+ * @param module module
+ */
+ public void setModule(final String module) {
+ this.module = module;
+ }
+
+ /**
+ * get responseContentLength.
+ *
+ * @return ResponseContentLength
+ */
+ public int getResponseContentLength() {
+ return responseContentLength;
+ }
+
+ /**
+ * set ResponseContentLength.
+ *
+ * @param responseContentLength ResponseContentLength
+ */
+ public void setResponseContentLength(final int responseContentLength) {
+ this.responseContentLength = responseContentLength;
+ }
+
+ /**
+ * get userAgent.
+ *
+ * @return userAgent
+ */
+ public String getUserAgent() {
+ return userAgent;
+ }
+
+ /**
+ * set userAgent.
+ *
+ * @param userAgent userAgent
+ */
+ public void setUserAgent(final String userAgent) {
+ this.userAgent = userAgent;
+ }
+
+ /**
+ * get host.
+ *
+ * @return host
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * set host.
+ *
+ * @param host host
+ */
+ public void setHost(final String host) {
+ this.host = host;
+ }
+
+ /**
+ * get clientIp.
+ *
+ * @return clientIp
+ */
+ public String getClientIp() {
+ return clientIp;
+ }
+
+ /**
+ * set clientIp.
+ *
+ * @param clientIp clientIp
+ */
+ public void setClientIp(final String clientIp) {
+ this.clientIp = clientIp;
+ }
+
+ /**
+ * get timeLocal.
+ *
+ * @return timeLocal
+ */
+ public String getTimeLocal() {
+ return timeLocal;
+ }
+
+ /**
+ * set timeLocal.
+ *
+ * @param timeLocal timeLocal
+ */
+ public void setTimeLocal(final String timeLocal) {
+ this.timeLocal = timeLocal;
+ }
+
+ /**
+ * get method.
+ *
+ * @return method
+ */
+ public String getMethod() {
+ return method;
+ }
+
+ /**
+ * set method.
+ *
+ * @param method method
+ */
+ public void setMethod(final String method) {
+ this.method = method;
+ }
+
+ /**
+ * get requestHeader.
+ *
+ * @return requestHeader
+ */
+ public String getRequestHeader() {
+ return requestHeader;
+ }
+
+ /**
+ * set requestHeader.
+ *
+ * @param requestHeader requestHeader
+ */
+ public void setRequestHeader(final String requestHeader) {
+ this.requestHeader = requestHeader;
+ }
+
+ /**
+ * get responseHeader.
+ *
+ * @return responseHeader
+ */
+ public String getResponseHeader() {
+ return responseHeader;
+ }
+
+ /**
+ * set responseHeader.
+ *
+ * @param responseHeader responseHeader
+ */
+ public void setResponseHeader(final String responseHeader) {
+ this.responseHeader = responseHeader;
+ }
+
+ /**
+ * get queryParams.
+ *
+ * @return queryParams
+ */
+ public String getQueryParams() {
+ return queryParams;
+ }
+
+ /**
+ * set queryParams.
+ *
+ * @param queryParams queryParams
+ */
+ public void setQueryParams(final String queryParams) {
+ this.queryParams = queryParams;
+ }
+
+ /**
+ * get requestBody.
+ *
+ * @return requestBody
+ */
+ public String getRequestBody() {
+ return requestBody;
+ }
+
+ /**
+ * set requestBody.
+ *
+ * @param requestBody requestBody
+ */
+ public void setRequestBody(final String requestBody) {
+ this.requestBody = requestBody;
+ }
+
+ /**
+ * get requestUri.
+ *
+ * @return requestUri
+ */
+ public String getRequestUri() {
+ return requestUri;
+ }
+
+ /**
+ * set requestUri.
+ *
+ * @param requestUri requestUri
+ */
+ public void setRequestUri(final String requestUri) {
+ this.requestUri = requestUri;
+ }
+
+ /**
+ * get responseBody.
+ *
+ * @return responseBody
+ */
+ public String getResponseBody() {
+ return responseBody;
+ }
+
+ /**
+ * set responseBody.
+ *
+ * @param responseBody responseBody
+ */
+ public void setResponseBody(final String responseBody) {
+ this.responseBody = responseBody;
+ }
+
+ /**
+ * get rpcType.
+ *
+ * @return rpcType
+ */
+ public String getRpcType() {
+ return rpcType;
+ }
+
+ /**
+ * set rpcType.
+ *
+ * @param rpcType rpcType
+ */
+ public void setRpcType(final String rpcType) {
+ this.rpcType = rpcType;
+ }
+
+ /**
+ * set status.
+ *
+ * @return status
+ */
+ public int getStatus() {
+ return status;
+ }
+
+ /**
+ * set status.
+ *
+ * @param status status
+ */
+ public void setStatus(final int status) {
+ this.status = status;
+ }
+
+ /**
+ * get upstreamIp.
+ *
+ * @return upstreamIp
+ */
+ public String getUpstreamIp() {
+ return upstreamIp;
+ }
+
+ /**
+ * set upstreamIp.
+ *
+ * @param upstreamIp upstreamIp
+ */
+ public void setUpstreamIp(final String upstreamIp) {
+ this.upstreamIp = upstreamIp;
+ }
+
+ /**
+ * get upstreamResponseTime.
+ *
+ * @return upstreamResponseTime
+ */
+ public long getUpstreamResponseTime() {
+ return upstreamResponseTime;
+ }
+
+ /**
+ * set UpstreamResponseTime.
+ *
+ * @param upstreamResponseTime upstreamResponseTime
+ */
+ public void setUpstreamResponseTime(final long upstreamResponseTime) {
+ this.upstreamResponseTime = upstreamResponseTime;
+ }
+
+ /**
+ * get traceId.
+ *
+ * @return traceId
+ */
+ public String getTraceId() {
+ return traceId;
+ }
+
+ /**
+ * set traceId.
+ *
+ * @param traceId tracing id
+ */
+ public void setTraceId(final String traceId) {
+ this.traceId = traceId;
+ }
+
+ /**
+ * get request path.
+ *
+ * @return request path
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * request path.
+ *
+ * @param path request path
+ */
+ public void setPath(final String path) {
+ this.path = path;
+ }
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/handler/LoggingElasticSearchPluginDataHandler.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/handler/LoggingElasticSearchPluginDataHandler.java
new file mode 100644
index 000000000..4ed3a1eae
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/handler/LoggingElasticSearchPluginDataHandler.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch.handler;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.common.dto.ConditionData;
+import org.apache.shenyu.common.dto.PluginData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.enums.PluginEnum;
+import org.apache.shenyu.common.enums.SelectorTypeEnum;
+import org.apache.shenyu.common.utils.GsonUtils;
+import org.apache.shenyu.plugin.base.handler.PluginDataHandler;
+import org.apache.shenyu.plugin.logging.elasticsearch.DefaultLogCollector;
+import org.apache.shenyu.plugin.logging.elasticsearch.config.LogCollectConfig;
+import org.apache.shenyu.plugin.logging.elasticsearch.constant.LoggingConstant;
+import org.apache.shenyu.plugin.logging.elasticsearch.elasticsearch.ElasticSearchLogCollectClient;
+import org.apache.shenyu.plugin.logging.elasticsearch.utils.LogCollectConfigUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * The type logging elasticsearch plugin data handler.
+ */
+public class LoggingElasticSearchPluginDataHandler implements PluginDataHandler {
+ private static final Logger LOG = LoggerFactory.getLogger(LoggingElasticSearchPluginDataHandler.class);
+
+ private static final ElasticSearchLogCollectClient ELASTICSEARCH_LOG_COLLECT_CLIENT = new ElasticSearchLogCollectClient();
+
+ private static final String EMPTY_JSON = "{}";
+
+ private static final Map<String, List<String>> SELECT_ID_URI_LIST_MAP = new ConcurrentHashMap<>();
+
+ private static final Map<String, LogCollectConfig.LogApiConfig> SELECT_API_CONFIG_MAP = new ConcurrentHashMap<>();
+
+ /**
+ * start or close elasticsearch client.
+ */
+ @Override
+ public void handlerPlugin(final PluginData pluginData) {
+ LOG.info("handler loggingElasticSearch Plugin data:{}", GsonUtils.getGson().toJson(pluginData));
+ if (pluginData.getEnabled()) {
+ LogCollectConfig.GlobalLogConfig globalLogConfig = GsonUtils.getInstance().fromJson(pluginData.getConfig(),
+ LogCollectConfig.GlobalLogConfig.class);
+
+ LogCollectConfigUtils.setGlobalConfig(globalLogConfig);
+ Properties properties = new Properties();
+ properties.setProperty(LoggingConstant.HOST, globalLogConfig.getHost());
+ properties.setProperty(LoggingConstant.PORT, globalLogConfig.getPort());
+ ELASTICSEARCH_LOG_COLLECT_CLIENT.initClient(properties);
+ DefaultLogCollector.getInstance().start();
+ } else {
+ try {
+ DefaultLogCollector.getInstance().close();
+ } catch (Exception e) {
+ LOG.error("close log collector error", e);
+ }
+ }
+ }
+
+ @Override
+ public void handlerSelector(final SelectorData selectorData) {
+ LOG.info("handler loggingElasticSearch selector data:{}", GsonUtils.getGson().toJson(selectorData));
+ String handleJson = selectorData.getHandle();
+ if (StringUtils.isEmpty(handleJson) || EMPTY_JSON.equals(handleJson.trim())) {
+ return;
+ }
+ if (selectorData.getType() != SelectorTypeEnum.CUSTOM_FLOW.getCode()
+ || CollectionUtils.isEmpty(selectorData.getConditionList())) {
+ return;
+ }
+
+ LogCollectConfig.LogApiConfig logApiConfig = GsonUtils.getInstance().fromJson(handleJson,
+ LogCollectConfig.LogApiConfig.class);
+ if (StringUtils.isBlank(logApiConfig.getIndex()) || StringUtils.isBlank(logApiConfig.getSampleRate())) {
+ return;
+ }
+ List<String> uriList = new ArrayList<>();
+ for (ConditionData conditionData : selectorData.getConditionList()) {
+ if ("uri".equals(conditionData.getParamType()) && StringUtils.isNotBlank(conditionData.getParamValue())
+ && ("match".equals(conditionData.getOperator()) || "=".equals(conditionData.getOperator()))) {
+ uriList.add(conditionData.getParamValue().trim());
+ }
+ }
+ SELECT_ID_URI_LIST_MAP.put(selectorData.getId(), uriList);
+ SELECT_API_CONFIG_MAP.put(selectorData.getId(), logApiConfig);
+ }
+
+ @Override
+ public void removeSelector(final SelectorData selectorData) {
+ LOG.info("handler remove loggingElasticSearch selector data:{}", GsonUtils.getGson().toJson(selectorData));
+ SELECT_ID_URI_LIST_MAP.remove(selectorData.getId());
+ SELECT_API_CONFIG_MAP.remove(selectorData.getId());
+ }
+
+ @Override
+ public String pluginNamed() {
+ return PluginEnum.LOGGING_ElasticSearch.getName();
+ }
+
+ /**
+ * get elasticsearch log collect client.
+ * @return elastcisearch log collect client.
+ */
+ public static ElasticSearchLogCollectClient getElasticSearchLogCollectClient() {
+ return ELASTICSEARCH_LOG_COLLECT_CLIENT;
+ }
+
+ /**
+ * get selectId uriList map.
+ * @return selectId uriList map
+ */
+ public static Map<String, List<String>> getSelectIdUriListMap() {
+ return SELECT_ID_URI_LIST_MAP;
+ }
+
+ /**
+ * get select api config map.
+ * @return select api config map
+ */
+ public static Map<String, LogCollectConfig.LogApiConfig> getSelectApiConfigMap() {
+ return SELECT_API_CONFIG_MAP;
+ }
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/sampler/CountSampler.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/sampler/CountSampler.java
new file mode 100644
index 000000000..15e0cc6c1
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/sampler/CountSampler.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch.sampler;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+
+import java.util.BitSet;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * used for sample log.
+ * reference resources: http://stackoverflow.com/questions/12817946/generate-a-random-bitset-with-n-1s
+ */
+public class CountSampler implements Sampler {
+
+ private final AtomicInteger counter;
+
+ private final BitSet sampleDecisions;
+
+ /**
+ * Fills a bitset with decisions according to the supplied probability.
+ */
+ CountSampler(final float probability) {
+ counter = new AtomicInteger();
+ int percent = (int) (probability * 100.0f);
+ this.sampleDecisions = genRandomBitSet(100, percent);
+ }
+
+ /**
+ * loops over the pre-canned decisions, resetting to zero when it gets to the end.
+ */
+ @Override
+ public boolean isSampled(final ServerHttpRequest request) {
+ return sampleDecisions.get(mod(counter.getAndIncrement()));
+ }
+
+ /**
+ * Returns a non-negative mod.
+ */
+ private int mod(final int dividend) {
+ int result = dividend % 100;
+ return result >= 0 ? result : 100 + result;
+ }
+
+ /**
+ * gen random bitSet.
+ * reference resources: http://stackoverflow.com/questions/12817946/generate-a-random-bitset-with-n-1s
+ *
+ * @param size bitmap size
+ * @param cardinality cardinality
+ * @return bitSet
+ */
+ private BitSet genRandomBitSet(final int size, final int cardinality) {
+ BitSet result = new BitSet(size);
+ int[] chosen = new int[cardinality];
+ int i;
+ for (i = 0; i < cardinality; ++i) {
+ chosen[i] = i;
+ result.set(i);
+ }
+ Random random = new Random();
+ for (; i < size; ++i) {
+ int j = random.nextInt(i + 1);
+ if (j < cardinality) {
+ result.clear(chosen[j]);
+ result.set(i);
+ chosen[j] = i;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * create a sampler instance.
+ *
+ * @param probability probability
+ * @return sampler instance
+ */
+ public static Sampler create(final String probability) {
+ if (StringUtils.isBlank(probability)) {
+ return ALWAYS_SAMPLE;
+ }
+ if ("0".equals(probability)) {
+ return NEVER_SAMPLE;
+ }
+ if ("1".equals(probability) || "1.0".equals(probability) || "1.0.0".equals(probability)) {
+ return ALWAYS_SAMPLE;
+ }
+ float parseProbability = NumberUtils.toFloat(probability, 1);
+ if (parseProbability < 0.01f || parseProbability > 1) {
+ throw new IllegalArgumentException(
+ "probability should be between 0.01 and 1: was " + probability);
+ }
+ return new CountSampler(parseProbability);
+ }
+
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/sampler/Sampler.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/sampler/Sampler.java
new file mode 100644
index 000000000..8112bfbac
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/sampler/Sampler.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch.sampler;
+
+import org.springframework.http.server.reactive.ServerHttpRequest;
+
+public interface Sampler {
+
+ Sampler ALWAYS_SAMPLE = request -> true;
+ Sampler NEVER_SAMPLE = request -> false;
+
+ /**
+ * judge a ServerHttpRequest should be sample.
+ *
+ * @param request request
+ * @return whether sample
+ */
+ boolean isSampled(ServerHttpRequest request);
+
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/utils/LogCollectConfigUtils.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/utils/LogCollectConfigUtils.java
new file mode 100644
index 000000000..00af59a6f
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/utils/LogCollectConfigUtils.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch.utils;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.plugin.logging.elasticsearch.config.LogCollectConfig.GlobalLogConfig;
+import org.apache.shenyu.plugin.logging.elasticsearch.sampler.CountSampler;
+import org.apache.shenyu.plugin.logging.elasticsearch.sampler.Sampler;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.util.AntPathMatcher;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * log collect config Utils.
+ */
+public final class LogCollectConfigUtils {
+
+ private static final AntPathMatcher MATCHER = new AntPathMatcher();
+
+ private static GlobalLogConfig globalLogConfig;
+
+ private static final GlobalLogConfig DEFAULT_GLOBAL_LOG_CONFIG =
+ new GlobalLogConfig();
+
+ private static Map<String, Sampler> apiSamplerMap = new HashMap<>();
+
+ private static Sampler globalSampler = Sampler.ALWAYS_SAMPLE;
+
+ private LogCollectConfigUtils() {
+ }
+
+ /**
+ * set global config.
+ * @param config global config
+ */
+ public static void setGlobalConfig(final GlobalLogConfig config) {
+ globalLogConfig = config;
+ }
+
+ /**
+ * set api sample.
+ * @param uriSampleMap api sample map
+ */
+ public static void setSampler(final Map<String, String> uriSampleMap) {
+ Map<String, Sampler> samplerMap = new HashMap<>();
+ uriSampleMap.forEach((path, sampler) -> {
+ if (StringUtils.isBlank(sampler)) {
+ samplerMap.put(path, globalSampler);
+ } else {
+ samplerMap.put(path, CountSampler.create(sampler));
+ }
+ });
+ apiSamplerMap = samplerMap;
+ }
+
+ /**
+ * set global Sampler.
+ * @param sampler global sampler
+ */
+ public static void setGlobalSampler(final String sampler) {
+ if (StringUtils.isNotBlank(sampler)) {
+ try {
+ globalSampler = CountSampler.create(sampler);
+ } catch (Exception e) {
+ globalSampler = Sampler.ALWAYS_SAMPLE;
+ }
+ }
+ }
+
+ /**
+ * judge whether sample.
+ *
+ * @param request request
+ * @return whether sample
+ */
+ public static boolean isSampled(final ServerHttpRequest request) {
+ String path = request.getURI().getPath();
+ for (Map.Entry<String, Sampler> entry : apiSamplerMap.entrySet()) {
+ String pattern = entry.getKey();
+ if (MATCHER.match(pattern, path)) {
+ return entry.getValue().isSampled(request);
+ }
+ }
+ if (globalSampler != null) {
+ return globalSampler.isSampled(request);
+ }
+ return true;
+ }
+
+ /**
+ * judge whether request org.apache.shenyu.plugin.logging.body too large.
+ *
+ * @param bodySize org.apache.shenyu.plugin.logging.body size
+ * @return whether request org.apache.shenyu.plugin.logging.body too large
+ */
+ public static boolean isRequestBodyTooLarge(final int bodySize) {
+ if (Objects.isNull(globalLogConfig)) {
+ return false;
+ }
+ return bodySize > globalLogConfig.getMaxRequestBody();
+ }
+
+ /**
+ * judge whether response org.apache.shenyu.plugin.logging.body too large.
+ *
+ * @param bodySize org.apache.shenyu.plugin.logging.body size.
+ * @return whether response org.apache.shenyu.plugin.logging.body too large
+ */
+ public static boolean isResponseBodyTooLarge(final int bodySize) {
+ if (Objects.isNull(globalLogConfig)) {
+ return false;
+ }
+ return bodySize > globalLogConfig.getMaxResponseBody();
+ }
+
+ /**
+ * get global log config.
+ *
+ * @return global log config
+ */
+ public static GlobalLogConfig getGlobalLogConfig() {
+ return Optional.ofNullable(globalLogConfig).orElse(DEFAULT_GLOBAL_LOG_CONFIG);
+ }
+}
diff --git a/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/utils/LogCollectUtils.java b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/utils/LogCollectUtils.java
new file mode 100644
index 000000000..414c76e1f
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-logging/shenyu-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/plugin/logging/elasticsearch/utils/LogCollectUtils.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.plugin.logging.elasticsearch.utils;
+
+import org.apache.shenyu.common.utils.JsonUtils;
+import org.springframework.http.HttpHeaders;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * log collect utils.
+ */
+public class LogCollectUtils {
+
+ private static final List<String> BINARY_TYPE_LIST = Arrays.asList("image", "multipart", "cbor",
+ "octet-stream", "pdf", "javascript", "css", "html");
+
+ /**
+ * judge whether is binary type.
+ *
+ * @param headers request or response header
+ * @return whether binary type
+ */
+ public static boolean isNotBinaryType(final HttpHeaders headers) {
+ return Optional.ofNullable(headers).map(HttpHeaders::getContentType)
+ .map(contentType -> !BINARY_TYPE_LIST.contains(contentType.getType())
+ && !BINARY_TYPE_LIST.contains(contentType.getSubtype()))
+ .orElse(true);
+ }
+
+ /**
+ * get request header string.
+ *
+ * @param headers request headers
+ * @return header string
+ */
+ public static String getHeaders(final HttpHeaders headers) {
+ Map<String, String> map = headers.entrySet().stream()
+ .collect(Collectors.toMap(Map.Entry::getKey, entry -> String.join(",", entry.getValue())));
+ return JsonUtils.toJson(map).replaceAll("\\\"", "");
+ }
+
+}
diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/pom.xml b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/pom.xml
index 502b795c5..3acd0a769 100644
--- a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/pom.xml
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/pom.xml
@@ -59,6 +59,7 @@
<module>shenyu-spring-boot-starter-plugin-mqtt</module>
<module>shenyu-spring-boot-starter-plugin-metrics</module>
<module>shenyu-spring-boot-starter-plugin-logging-rocketmq</module>
+ <module>shenyu-spring-boot-starter-plugin-logging-elasticsearch</module>
<module>shenyu-spring-boot-starter-plugin-cache</module>
<module>shenyu-spring-boot-starter-plugin-mock</module>
</modules>
diff --git a/shenyu-plugin/shenyu-plugin-logging/pom.xml b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-logging-elasticsearch/pom.xml
similarity index 51%
copy from shenyu-plugin/shenyu-plugin-logging/pom.xml
copy to shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-logging-elasticsearch/pom.xml
index dddc7a374..ef062e273 100644
--- a/shenyu-plugin/shenyu-plugin-logging/pom.xml
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-logging-elasticsearch/pom.xml
@@ -16,20 +16,31 @@
~ limitations under the License.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.shenyu</groupId>
- <artifactId>shenyu-plugin</artifactId>
+ <artifactId>shenyu-spring-boot-starter-plugin</artifactId>
<version>2.5.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
- <artifactId>shenyu-plugin-logging</artifactId>
- <packaging>pom</packaging>
- <modules>
- <module>shenyu-plugin-logging-console</module>
- <module>shenyu-plugin-logging-rocketmq</module>
- </modules>
+ <artifactId>shenyu-spring-boot-starter-plugin-logging-elasticsearch</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-plugin-logging-elasticsearch</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
</project>
\ No newline at end of file
diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/springboot/starter/plugin/logging/elasticsearch/LoggingElasticSearchPluginConfiguration.java b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/springboot/starter/plugin/logging/elasticsearch/LoggingElasticSearchPluginConfiguration.java
new file mode 100644
index 000000000..62c2a24b4
--- /dev/null
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-logging-elasticsearch/src/main/java/org/apache/shenyu/springboot/starter/plugin/logging/elasticsearch/LoggingElasticSearchPluginConfiguration.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.springboot.starter.plugin.logging.elasticsearch;
+
+import org.apache.shenyu.plugin.api.ShenyuPlugin;
+import org.apache.shenyu.plugin.base.handler.PluginDataHandler;
+import org.apache.shenyu.plugin.logging.elasticsearch.LoggingElasticSearchPlugin;
+import org.apache.shenyu.plugin.logging.elasticsearch.handler.LoggingElasticSearchPluginDataHandler;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * config logging ElasticSearch plugin.
+ */
+@Configuration
+@ConditionalOnProperty(value = {"shenyu.plugins.logging-elasticsearch.enabled"}, havingValue = "true", matchIfMissing = true)
+public class LoggingElasticSearchPluginConfiguration {
+
+ /**
+ * logging elasticsearch plugin data handler.
+ * @return logging elasticsearch PluginDataHandler
+ */
+ @Bean
+ public PluginDataHandler loggingElasticSearchPluginDataHandler() {
+ return new LoggingElasticSearchPluginDataHandler();
+ }
+
+ /**
+ * Logging ElasticSearch plugin.
+ * @return LoggingElasticSearch
+ */
+ @Bean
+ public ShenyuPlugin loggingElasticSearchPlugin() {
+ return new LoggingElasticSearchPlugin();
+ }
+}
diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-logging-elasticsearch/src/main/resources/META-INF/spring.factories b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-logging-elasticsearch/src/main/resources/META-INF/spring.factories
new file mode 100644
index 000000000..a77d19005
--- /dev/null
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-logging-elasticsearch/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.apache.shenyu.springboot.starter.plugin.logging.elasticsearch.LoggingElasticSearchPluginConfiguration
diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-logging-elasticsearch/src/main/resources/META-INF/spring.provides b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-logging-elasticsearch/src/main/resources/META-INF/spring.provides
new file mode 100644
index 000000000..6061f34fe
--- /dev/null
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-logging-elasticsearch/src/main/resources/META-INF/spring.provides
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+provides: shenyu-spring-boot-starter-plugin-logging-elasticsearch