You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2022/04/23 09:00:45 UTC

[skywalking] branch master updated: Add data-generator module to run OAP in testing mode, generating mock data for testing (#8725)

This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git


The following commit(s) were added to refs/heads/master by this push:
     new 3256b6c9d9 Add data-generator module to run OAP in testing mode, generating mock data for testing (#8725)
3256b6c9d9 is described below

commit 3256b6c9d9b202ca676554ef2f1eac822c8a882f
Author: kezhenxu94 <ke...@apache.org>
AuthorDate: Sat Apr 23 17:00:32 2022 +0800

    Add data-generator module to run OAP in testing mode, generating mock data for testing (#8725)
---
 Makefile                                           |   4 +-
 apm-dist/src/main/assembly/binary.xml              |  25 +++
 docker/data-generator/Dockerfile                   |  52 ++++++
 docker/data-generator/docker-entrypoint.sh         |  46 +++++
 .../pom.xml => docker/data-generator/log4j2.xml    |  31 +--
 docs/en/changes/changes-9.0.0.md                   |   2 +-
 docs/en/changes/changes.md                         |   1 +
 docs/en/setup/backend/backend-data-generator.md    | 207 +++++++++++++++++++++
 docs/en/setup/backend/logs-template.json           |  83 +++++++++
 docs/en/setup/backend/segment-template.json        | 145 +++++++++++++++
 docs/menu.yml                                      |   6 +-
 .../skywalking/oap/server/core/source/Segment.java |   2 +
 .../oap/server/library/server/http/HTTPServer.java |   2 +-
 oap-server/server-tools/data-generator/pom.xml     |  55 ++++++
 .../data-generator/src/main/assembly/bin/start.sh  |  49 +++++
 .../apache/skywalking/generator/BoolGenerator.java |  54 ++++++
 .../skywalking/generator/FixedStringGenerator.java |  51 +++++
 .../org/apache/skywalking/generator/Generator.java |  44 +++++
 .../apache/skywalking/generator/IntGenerator.java  | 102 ++++++++++
 .../apache/skywalking/generator/ListGenerator.java |  60 ++++++
 .../skywalking/generator/SequenceGenerator.java    | 118 ++++++++++++
 .../skywalking/generator/StringGenerator.java      |  90 +++++++++
 .../apache/skywalking/generator/UUIDGenerator.java |  70 +++++++
 .../skywalking/module/DataGeneratorModule.java     |  35 ++++
 .../module/DataGeneratorModuleProvider.java        |  80 ++++++++
 .../skywalking/restapi/LogGeneratorHandler.java    | 133 +++++++++++++
 .../org/apache/skywalking/restapi/LogRequest.java  | 105 +++++++++++
 .../restapi/SegmentGeneratorHandler.java           | 134 +++++++++++++
 .../apache/skywalking/restapi/SegmentRequest.java  | 100 ++++++++++
 .../apache/skywalking/restapi/SpanGenerator.java   |  85 +++++++++
 .../apache/skywalking/restapi/TagGenerator.java    |  45 +++++
 .../skywalking/starter/DataGeneratorStartUp.java   |  28 +++
 ...ywalking.oap.server.library.module.ModuleDefine |  19 ++
 ...alking.oap.server.library.module.ModuleProvider |  19 ++
 .../src/main/resources/application.yml             | 179 ++++++++++++++++++
 .../skywalking/generator/IntGeneratorTest.java     |  36 ++++
 .../generator/SequenceGeneratorTest.java           |  55 ++++++
 oap-server/server-tools/pom.xml                    |   1 +
 38 files changed, 2331 insertions(+), 22 deletions(-)

diff --git a/Makefile b/Makefile
index f18e728066..3caefdf20f 100644
--- a/Makefile
+++ b/Makefile
@@ -41,6 +41,7 @@ DOCKER_BUILD_TOP:=${CONTEXT}/docker_build
 HUB ?= skywalking
 OAP_NAME ?= oap
 UI_NAME ?= ui
+DATA_GENERATOR_NAME ?= data-generator
 TAG ?= latest
 
 .PHONY: docker docker.all
@@ -57,6 +58,7 @@ BUILD_ARGS := $(BUILD_ARGS) --build-arg DIST=$(DIST) --build-arg SKYWALKING_CLI_
 
 %.ui: NAME = $(UI_NAME)
 %.oap: NAME = $(OAP_NAME)
+%.data-generator: NAME = $(DATA_GENERATOR_NAME)
 
 docker.%: PLATFORMS =
 docker.%: LOAD_OR_PUSH = --load
@@ -86,4 +88,4 @@ define DOCKER_RULE
 		-t $(HUB)/$(NAME):latest \
 		$(DOCKER_BUILD_TOP)/$(NAME)
 	docker buildx rm skywalking_main || true
-endef
\ No newline at end of file
+endef
diff --git a/apm-dist/src/main/assembly/binary.xml b/apm-dist/src/main/assembly/binary.xml
index 9fd3e92a3e..d067970f74 100644
--- a/apm-dist/src/main/assembly/binary.xml
+++ b/apm-dist/src/main/assembly/binary.xml
@@ -82,6 +82,31 @@
             <outputDirectory>oap-libs</outputDirectory>
         </fileSet>
 
+        <!-- data generator -->
+        <fileSet>
+            <directory>${project.basedir}/../oap-server/server-tools/data-generator/target/</directory>
+            <includes>
+                <include>data-generator-${project.version}.jar</include>
+            </includes>
+            <outputDirectory>oap-libs</outputDirectory>
+        </fileSet>
+        <fileSet>
+            <directory>${project.basedir}/../oap-server/server-tools/data-generator/src/main/assembly/bin</directory>
+            <outputDirectory>tools/data-generator/bin</outputDirectory>
+            <includes>
+                <include>*.sh</include>
+            </includes>
+            <fileMode>0755</fileMode>
+        </fileSet>
+        <fileSet>
+            <directory>${project.basedir}/../oap-server/server-tools/data-generator/src/main/resources</directory>
+            <outputDirectory>tools/data-generator/config</outputDirectory>
+            <includes>
+                <include>application.yml</include>
+            </includes>
+        </fileSet>
+        <!-- data generator -->
+
         <!-- Profile exporter tools -->
         <fileSet>
             <directory>${project.basedir}/../tools/profile-exporter</directory>
diff --git a/docker/data-generator/Dockerfile b/docker/data-generator/Dockerfile
new file mode 100644
index 0000000000..9f9a58152b
--- /dev/null
+++ b/docker/data-generator/Dockerfile
@@ -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.
+
+ARG BASE_IMAGE='eclipse-temurin:11-jre'
+
+ARG SKYWALKING_CLI_VERSION
+
+FROM apache/skywalking-cli:$SKYWALKING_CLI_VERSION as cli
+
+FROM $BASE_IMAGE
+
+WORKDIR /skywalking
+
+ENV JAVA_OPTS=" -Xms2G "
+
+ARG DIST
+
+COPY "$DIST" .
+
+RUN set -ex; \
+    tar -xzf "$DIST" --strip 1; \
+    rm -rf "$DIST"; \
+    rm -rf "config/log4j2.xml"; \
+    rm -rf "bin"; \
+    rm -rf "webapp"; \
+    rm -rf "agent"; \
+    mkdir "bin";
+
+COPY --from=cli /swctl ./bin
+
+COPY log4j2.xml config/
+COPY docker-entrypoint.sh .
+
+RUN mkdir ext-config; \
+    mkdir ext-libs;
+
+EXPOSE 12800 11800 1234
+
+ENTRYPOINT ["bash", "docker-entrypoint.sh"]
diff --git a/docker/data-generator/docker-entrypoint.sh b/docker/data-generator/docker-entrypoint.sh
new file mode 100755
index 0000000000..a6c986d303
--- /dev/null
+++ b/docker/data-generator/docker-entrypoint.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+# 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.
+
+set -e
+
+echo "[Entrypoint] Apache SkyWalking Data Generator Docker Image"
+
+EXT_LIB_DIR=/skywalking/ext-libs
+EXT_CONFIG_DIR=/skywalking/ext-config
+
+# Override default application.yml with application.yml in the data generator module
+
+cp -vfRL tools/data-generator/config/application.yml config/
+
+# Override configuration files
+if [ "$(ls -A $EXT_CONFIG_DIR)" ]; then
+  cp -vfRL ${EXT_CONFIG_DIR}/* config/
+fi
+
+CLASSPATH="config:$CLASSPATH"
+for i in oap-libs/*.jar
+do
+    CLASSPATH="$i:$CLASSPATH"
+done
+for i in "${EXT_LIB_DIR}"/*.jar
+do
+    CLASSPATH="$i:$CLASSPATH"
+done
+
+set -ex
+
+exec java ${JAVA_OPTS} -classpath ${CLASSPATH} org.apache.skywalking.starter.DataGeneratorStartUp "$@"
diff --git a/oap-server/server-tools/pom.xml b/docker/data-generator/log4j2.xml
similarity index 60%
copy from oap-server/server-tools/pom.xml
copy to docker/data-generator/log4j2.xml
index 4371350c79..6152fc75c2 100644
--- a/oap-server/server-tools/pom.xml
+++ b/docker/data-generator/log4j2.xml
@@ -17,18 +17,19 @@
   ~
   -->
 
-<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>
-        <artifactId>oap-server</artifactId>
-        <groupId>org.apache.skywalking</groupId>
-        <version>9.1.0-SNAPSHOT</version>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-
-    <artifactId>server-tools</artifactId>
-    <packaging>pom</packaging>
-
-    <modules>
-        <module>profile-exporter</module>
-    </modules>
-</project>
+<Configuration status="INFO">
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout>
+                <LevelPatternSelector defaultPattern="%d %c %L [%t] %-5p %x - %m%n">
+                    <PatternMatch key="ERROR" pattern="%d %c %L [%t] %-5p %x - [%swversion] %m%n" />
+                </LevelPatternSelector>
+            </PatternLayout>
+        </Console>
+    </Appenders>
+    <Loggers>
+        <Root level="INFO">
+            <AppenderRef ref="Console"/>
+        </Root>
+    </Loggers>
+</Configuration>
diff --git a/docs/en/changes/changes-9.0.0.md b/docs/en/changes/changes-9.0.0.md
index 933e2cde50..d3b59d325c 100644
--- a/docs/en/changes/changes-9.0.0.md
+++ b/docs/en/changes/changes-9.0.0.md
@@ -125,7 +125,7 @@
 * Add `Column.shardingKeyIdx` for column definition for BanyanDB.
 
 ```
-Sharding key is used to group time series data per metric of one entity in one place (same sharding and/or same 
+Sharding key is used to group time series data per metric of one entity in one place (same sharding and/or same
 row for column-oriented database).
 For example,
 ServiceA's traffic gauge, service call per minute, includes following timestamp values, then it should be sharded by service ID
diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index 5d22d10b95..08684ac452 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -27,6 +27,7 @@
   priority**](https://github.com/apache/skywalking/pull/8664) to avoid consuming issues.
 * Fix the problem that some configurations (such as group.id) did not take effect due to the override order when using the kafkaConsumerConfig property to extend the configuration in Kafka Fetcher.
 * Remove build time from the OAP version.
+* Add data-generator module to run OAP in testing mode, generating mock data for testing.
 
 #### UI
 
diff --git a/docs/en/setup/backend/backend-data-generator.md b/docs/en/setup/backend/backend-data-generator.md
new file mode 100644
index 0000000000..a2084bd27b
--- /dev/null
+++ b/docs/en/setup/backend/backend-data-generator.md
@@ -0,0 +1,207 @@
+# Mock data generator for testing
+
+In 9.1.0, SkyWalking adds a module to generate mock data for testing. You can use this module to generate
+mock data that will be sent to the storage.
+
+To start the data generator, execute the script `tools/data-generator/bin/start.sh`.
+
+Note that SkyWalking doesn't release a Docker image for this module, but you can still build it yourselves
+by running the commands:
+
+```shell
+# build a Docker image for local use
+make docker.data-generator
+
+# or push to your registry
+export HUB=<your-registry>
+make push.docker.data-generator
+```
+
+Currently the module can generate two kinds of SkyWalking data, segments and logs. For each type,
+there are some generators that can be used to fill the fields.
+
+## Generate mock data
+
+To generate mock data, `POST` a request to URL path `/mock-data/segments/tasks` (segments) or
+`/mock-data/logs/tasks` (logs) with a generator template:
+
+```shell
+curl -XPOST 'http://localhost:12800/mock-data/segments/tasks?size=20' -H'Content-Type: application/json' -d "@segment-template.json"
+
+curl -XPOST 'http://localhost:12800/mock-data/logs/tasks?size=20' -H'Content-Type: application/json' -d "@logs-template.json"
+```
+
+There are two possible types of task to generate mock data, `size` and `qps`:
+
+- `size` (`/mock-data/segments/tasks?size=20`): the task will generate total number of `size` segments/logs and then finish.
+- `qps` (`/mock-data/segments/tasks?qps=20`): the task will generate `qps` segments/logs per second continuously, until the task is [cancelled](#cancel-a-task).
+
+Refer to [the segment template](segment-template.json), [the log template](logs-template.json) and the [Generators](#generators) for more details
+about how to compose a template.
+
+## Cancel a task
+
+When the task is acknowledged by the server it will return a task id that can be used to cancelled
+the task by sending a `DELETE` request to URL path `/mock-data/logs/tasks` with a parameter `requestId` (i.e.
+`/mock-data/logs/tasks?requestId={request id returned in previous request}`):
+
+```shell
+curl -XDELETE 'http://localhost:12800/mock-data/segments/task?requestId=70d8a39e-b51e-49de-a6fc-43abf80482c1'
+curl -XDELETE 'http://localhost:12800/mock-data/logs/task?requestId=70d8a39e-b51e-49de-a6fc-43abf80482c1'
+```
+
+## Cancel all tasks
+
+When needed, you can also send a `DELETE` request to path `/mock-data/segments/tasks` to cancel all segment tasks.
+
+```shell
+curl -XDELETE 'http://localhost:12800/mock-data/segments/tasks
+curl -XDELETE 'http://localhost:12800/mock-data/logs/tasks
+```
+
+## Generators
+
+### `uuid`
+
+`uuid` generator leverages `java.util.UUID` to generate a string. You can use `uuid` generator to fill the
+`traceId` field of segments.
+
+`changingFrequency` property can be used when you want to reuse a `uuid` for multiple times, for example,
+if you want a `traceId` to be reused by 5 segments, then setting `changingFrequency` to `5` would do the trick.
+By setting `changingFrequency` to `5`, `uuid` generates 1 string, and uses it for 5 times, then re-generates
+a new uuid string and uses it for another 5 times.
+
+```json
+"traceId": {
+    "type": "uuid",
+    "changingFrequency": "5"
+}
+```
+
+### `randomString` (`String`)
+
+#### `length` (`int`)
+
+`length` specifies the length of the random string to be generated,
+i.e. `generatedString.length() == length` is always `true`.
+
+#### `prefix` (`String`)
+
+`prefix` is always added to the random strings **after** they are generated, that means:
+
+- `generatedString.startsWith(prefix)` is always `true`, and,
+- `generatedString.length() == length + prefix.length()` is always true.
+
+#### `letters` (`boolean`)
+
+Specifies whether the random string contains letters (i.e. `a-zA-Z`).
+
+#### `numbers` (`boolean`)
+
+Specifies whether the random string contains numbers (i.e. `0-9`).
+
+#### `domainSize` (`int`)
+
+When generating random strings, you might just want some random strings and use them over and over again randomly,
+by setting `domainSize`, the generator generates `domainSize` random strings, and pick them randomly every time
+you need a string.
+
+### `randomBool` (`boolean`)
+
+This generator generates a `Boolean` value, `true` or `false` with a default possibility of 50%, while you can change the `possibility` below.
+
+#### `possibility` (`double`, `[0, 1]`)
+
+`possibility` is a `double` value `>= 0` and `<= 1`, it's `0.5` by default, meaning **about** half of the generated values are `true`.
+
+To always return a fixed boolean value `true`, you can just set the `possibility` to `1`, to always return a fixed boolean value `false`, you can set the `possibility` to `0`
+
+```json
+"error": {
+    "type": "randomBool",
+    "possibility": "0.9"
+}
+```
+
+> 90 percent of the generated values are `true`.
+
+### `randomInt` (`long`)
+
+#### `min` (`long`)
+
+The minimum value of the random integers, meaning all generated values satisfy `generatedInt >= min`.
+
+#### `max` (`long`)
+
+The maximum value of the random integers, meaning all generated values satisfy `generatedInt < min`.
+
+#### `domainSize` (`int`)
+
+This is similar to [`randomString`'s `domainSize`](#domainsize-int).
+
+### `randomList` (`list` / `array`)
+
+#### `size` (`int`)
+
+The list size of the generated list, i.e. `generatedList.size() == size`.
+
+#### `item` (`object`)
+
+`item` is a template that will be use as a prototype to generate the list items, for example when generating a
+list of `Tag`, the `item` should be the prototype of `Tag`, which can be composed by the generators again.
+
+```json
+"tags": {
+    "type": "randomList",
+    "size": 5,
+    "item": {
+        "key": {
+            "type": "randomString",
+            "length": "10",
+            "prefix": "test_tag_",
+            "letters": true,
+            "numbers": true,
+            "domainSize": 10
+        },
+        "value": {
+            "type": "randomString",
+            "length": "10",
+            "prefix": "test_value_",
+            "letters": true,
+            "numbers": true
+        }
+    }
+}
+```
+
+### `fixedString` (`string`)
+
+This generator always returns a fixed `value` of string.
+
+### `sequence` (`long`)
+
+`sequence` generator generates a sequence of monotonically increasing integers, with a configurable `fluctuation`.
+
+#### `min` (`long`)
+
+The minimum value of the sequence.
+
+#### `max` (`long`)
+
+The maximum value of the sequence.
+
+#### `step` (`long`)
+
+The increasing step of this sequence, i.e. `the next generated value == the previous value + step`.
+
+#### `domainSize` (`int`)
+
+This is similar to [`randomString`'s `domainSize`](#domainsize-int).
+
+#### `fluctuation` (`int`)
+
+By default, sequence is strictly increasing numbers, but in some cases you might want the numbers to fluctuate
+slightly while they are increasing. Adding property `fluctuation` to the generator will add a random number
+`>= -fluctuation, <= fluctuation` to the sequence elements.
+
+For example, `min = 10, max = 15, step = 1` generates a sequence `[10, 11, 12, 13, 14, 15]`, but adding `fluctuation = 2` **might** generate a sequence `[10, 12, 11, 14, 13, 15]`.
diff --git a/docs/en/setup/backend/logs-template.json b/docs/en/setup/backend/logs-template.json
new file mode 100644
index 0000000000..28308b32db
--- /dev/null
+++ b/docs/en/setup/backend/logs-template.json
@@ -0,0 +1,83 @@
+{
+    "timestamp": {
+        "type": "sequence",
+        "min": "1649643929000",
+        "max": "1649653929000"
+    },
+    "serviceName": {
+        "type": "randomString",
+        "length": "20",
+        "prefix": "test_svc_name_",
+        "letters": true,
+        "numbers": true
+    },
+    "serviceInstanceName": {
+        "type": "randomString",
+        "length": "20",
+        "prefix": "test_svc_inst_name_",
+        "letters": true,
+        "numbers": true
+    },
+    "endpointName": {
+        "type": "randomString",
+        "length": "20",
+        "prefix": "test_endpoint_",
+        "letters": true,
+        "numbers": true
+    },
+    "traceId": {
+        "type": "randomString",
+        "length": "20",
+        "prefix": "test_trace_id_",
+        "letters": true,
+        "numbers": true
+    },
+    "traceSegmentId": {
+        "type": "randomString",
+        "length": "20",
+        "prefix": "test",
+        "letters": true,
+        "numbers": true
+    },
+    "spanId": {
+        "type": "randomInt",
+        "min": "0",
+        "max": "5"
+    },
+    "contentType": {
+        "type": "randomInt",
+        "min": 1,
+        "max": 1
+    },
+    "content": {
+        "type": "randomString",
+        "length": "10",
+        "prefix": "test",
+        "letters": true,
+        "numbers": true
+    },
+    "error": {
+        "type": "randomBool"
+    },
+    "tags": {
+        "type": "randomList",
+        "size": 5,
+        "item": {
+            "key": {
+                "type": "randomString",
+                "length": "10",
+                "prefix": "test",
+                "letters": true,
+                "numbers": true,
+                "domainSize": 10
+            },
+            "value": {
+                "type": "randomString",
+                "length": "10",
+                "prefix": "test",
+                "letters": true,
+                "numbers": true
+            }
+        }
+    }
+}
diff --git a/docs/en/setup/backend/segment-template.json b/docs/en/setup/backend/segment-template.json
new file mode 100644
index 0000000000..88b1e5cfc6
--- /dev/null
+++ b/docs/en/setup/backend/segment-template.json
@@ -0,0 +1,145 @@
+{
+    "traceId": {
+        "type": "uuid",
+        "changingFrequency": "5"
+    },
+    "serviceInstanceName": {
+        "type": "randomString",
+        "length": "10",
+        "letters": true,
+        "numbers": true
+    },
+    "segmentId": {
+        "type": "randomString",
+        "length": "20",
+        "letters": true,
+        "numbers": true
+    },
+    "serviceName": {
+        "type": "randomString",
+        "length": "10",
+        "letters": true,
+        "numbers": true
+    },
+    "endpointName": {
+        "type": "randomString",
+        "length": "10",
+        "prefix": "test_",
+        "letters": true,
+        "numbers": true,
+        "domainSize": 10
+    },
+    "startTime": {
+        "type": "sequence",
+        "min": "1650510807000",
+        "max": "1650512807000"
+    },
+    "latency": {
+        "type": "randomInt",
+        "min": 100,
+        "max": 1000
+    },
+    "error": {
+        "type": "randomInt",
+        "min": 1,
+        "max": 1
+    },
+    "tags": {
+        "type": "randomList",
+        "size": 5,
+        "item": {
+            "key": {
+                "type": "randomString",
+                "length": "10",
+                "prefix": "test_tag_",
+                "letters": true,
+                "numbers": true,
+                "domainSize": 10
+            },
+            "value": {
+                "type": "randomString",
+                "length": "10",
+                "prefix": "test_value_",
+                "letters": true,
+                "numbers": true
+            }
+        }
+    },
+    "spans": {
+        "type": "randomList",
+        "size": 5,
+        "item": {
+            "spanId": {
+                "type": "sequence",
+                "min": "0",
+                "max": "5"
+            },
+            "parentSpanId": {
+                "type": "sequence",
+                "min": "-1",
+                "max": "4"
+            },
+            "startTime": {
+                "type": "randomInt",
+                "min": "1650510807000",
+                "max": "1650512807000",
+                "domainSize": 10
+            },
+            "endTime": {
+                "type": "randomInt",
+                "min": "1650510807000",
+                "max": "1650512807000",
+                "domainSize": 10
+            },
+            "operationName": {
+                "type": "randomString",
+                "length": "10",
+                "prefix": "test_endpoint_",
+                "letters": true,
+                "numbers": true
+            },
+            "peer": {
+                "type": "randomString",
+                "length": "10",
+                "prefix": "test_peer_",
+                "letters": true,
+                "numbers": true
+            },
+            "spanLayer": {
+                "type": "randomInt",
+                "min": "0",
+                "max": "4"
+            },
+            "componentId": {
+                "type": "randomInt",
+                "min": "0",
+                "max": "4"
+            },
+            "error": {
+                "type": "randomBool",
+                "possibility": "0.9"
+            },
+            "tags": {
+                "type": "randomList",
+                "size": 5,
+                "item": {
+                    "key": {
+                        "type": "randomString",
+                        "length": "10",
+                        "prefix": "test_tag_key_",
+                        "letters": true,
+                        "numbers": true,
+                        "domainSize": 10
+                    },
+                    "value": {
+                        "type": "randomString",
+                        "length": "10",
+                        "prefix": "test_tag_val_",
+                        "letters": true,
+                        "numbers": true
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/docs/menu.yml b/docs/menu.yml
index 7d3d19d5df..4854a72525 100644
--- a/docs/menu.yml
+++ b/docs/menu.yml
@@ -179,6 +179,8 @@ catalog:
                 path: "/en/setup/backend/backend-health-check"
       - name: "CLI Setup"
         path: "https://github.com/apache/skywalking-cli"
+      - name: "Mock Data Generator Setup"
+        path: "/en/setup/backend/backend-data-generator"
   - name: "Contributing Guides"
     catalog:
       - name: "Contribute"
@@ -237,7 +239,3 @@ catalog:
         path: "/en/changes/changes-6.x"
       - name: "5.x"
         path: "/en/changes/changes-5.x"
-
-
-
-
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/Segment.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/Segment.java
index 6249be8464..fc2bd8a0e7 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/Segment.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/Segment.java
@@ -22,10 +22,12 @@ import java.util.ArrayList;
 import java.util.List;
 import lombok.Getter;
 import lombok.Setter;
+import lombok.ToString;
 import org.apache.skywalking.oap.server.core.analysis.manual.searchtag.Tag;
 
 import static org.apache.skywalking.oap.server.core.source.DefaultScopeDefine.SEGMENT;
 
+@ToString
 @ScopeDeclaration(id = SEGMENT, name = "Segment")
 public class Segment extends Source {
 
diff --git a/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/http/HTTPServer.java b/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/http/HTTPServer.java
index 7f59096c57..f6749e100f 100644
--- a/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/http/HTTPServer.java
+++ b/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/http/HTTPServer.java
@@ -60,7 +60,7 @@ public class HTTPServer implements Server {
             .http1MaxHeaderSize(config.getMaxRequestHeaderSize())
             .idleTimeout(Duration.ofMillis(config.getIdleTimeOut()))
             .decorator(Route.ofCatchAll(), (delegate, ctx, req) -> {
-                if (!this.allowedMethods.contains(ctx.method())) {
+                if (!allowedMethods.contains(ctx.method())) {
                     return HttpResponse.of(HttpStatus.METHOD_NOT_ALLOWED);
                 }
                 return delegate.serve(ctx, req);
diff --git a/oap-server/server-tools/data-generator/pom.xml b/oap-server/server-tools/data-generator/pom.xml
new file mode 100644
index 0000000000..ce9fced371
--- /dev/null
+++ b/oap-server/server-tools/data-generator/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ 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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>server-tools</artifactId>
+        <groupId>org.apache.skywalking</groupId>
+        <version>9.1.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>data-generator</artifactId>
+    <name>data-generator</name>
+    <description>
+        Module to generate data for testing.
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>server-starter</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>application.yml</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/oap-server/server-tools/data-generator/src/main/assembly/bin/start.sh b/oap-server/server-tools/data-generator/src/main/assembly/bin/start.sh
new file mode 100644
index 0000000000..da1a78d295
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/assembly/bin/start.sh
@@ -0,0 +1,49 @@
+#!/usr/bin/env sh
+# 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.
+
+PRG="$0"
+PRGDIR=$(dirname "$PRG")
+[ -z "$OAP_HOME" ] && OAP_HOME=$(cd "$PRGDIR/../../.." > /dev/null || exit 1; pwd)
+
+OAP_LOG_DIR="${OAP_LOG_DIR:-${OAP_HOME}/logs}"
+JAVA_OPTS="${JAVA_OPTS:-  -Xms256M -Xmx512M}"
+
+if [ ! -d "${OAP_LOG_DIR}" ]; then
+    mkdir -p "${OAP_LOG_DIR}"
+fi
+
+_RUNJAVA=${JAVA_HOME}/bin/java
+[ -z "$JAVA_HOME" ] && _RUNJAVA=java
+
+CLASSPATH="$OAP_HOME/tools/data-generator/config:$OAP_HOME/config:$CLASSPATH"
+for i in "$OAP_HOME"/oap-libs/*.jar
+do
+    CLASSPATH="$i:$CLASSPATH"
+done
+
+OAP_OPTIONS=" -Doap.logDir=${OAP_LOG_DIR}"
+
+eval exec "\"$_RUNJAVA\" ${JAVA_OPTS} ${OAP_OPTIONS} -classpath $CLASSPATH org.apache.skywalking.starter.DataGeneratorStartUp \
+        2>${OAP_LOG_DIR}/oap.log 1> /dev/null &"
+
+if [ $? -eq 0 ]; then
+    sleep 1
+	echo "SkyWalking OAP started successfully!"
+else
+	echo "SkyWalking OAP started failure!"
+	exit 1
+fi
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/BoolGenerator.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/BoolGenerator.java
new file mode 100644
index 0000000000..7bda23aa7a
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/BoolGenerator.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.skywalking.generator;
+
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import lombok.Data;
+
+@JsonDeserialize(builder = BoolGenerator.Builder.class)
+public final class BoolGenerator implements Generator<Boolean> {
+    private final Random random = ThreadLocalRandom.current();
+    private final double possibility;
+
+    public BoolGenerator(Builder builder) {
+        possibility = builder.possibility;
+    }
+
+    @Override
+    public Boolean next() {
+        return random.nextDouble() < possibility;
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(next());
+    }
+
+    @Data
+    public static class Builder {
+        private double possibility = .5;
+
+        public BoolGenerator build() {
+            return new BoolGenerator(this);
+        }
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/FixedStringGenerator.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/FixedStringGenerator.java
new file mode 100644
index 0000000000..edf11a54ce
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/FixedStringGenerator.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.skywalking.generator;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import lombok.Data;
+
+@JsonDeserialize(builder = FixedStringGenerator.Builder.class)
+public final class FixedStringGenerator implements Generator<String> {
+    private final String value;
+
+    public FixedStringGenerator(Builder builder) {
+        value = builder.value;
+    }
+
+    @Override
+    public String next() {
+        return value;
+    }
+
+    @Data
+    public static class Builder {
+        private String value;
+
+        public FixedStringGenerator build() {
+            return new FixedStringGenerator(this);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(next());
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/Generator.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/Generator.java
new file mode 100644
index 0000000000..511f18873c
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/Generator.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.skywalking.generator;
+
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+@JsonTypeInfo(
+    use = JsonTypeInfo.Id.NAME,
+    include = JsonTypeInfo.As.PROPERTY,
+    property = "type")
+@JsonSubTypes({
+    @Type(value = StringGenerator.class, name = "randomString"),
+    @Type(value = BoolGenerator.class, name = "randomBool"),
+    @Type(value = IntGenerator.class, name = "randomInt"),
+    @Type(value = ListGenerator.class, name = "randomList"),
+    @Type(value = FixedStringGenerator.class, name = "fixedString"),
+    @Type(value = SequenceGenerator.class, name = "sequence"),
+    @Type(value = UUIDGenerator.class, name = "uuid"),
+})
+public interface Generator<T> {
+    public T next();
+
+    default public void reset() {
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/IntGenerator.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/IntGenerator.java
new file mode 100644
index 0000000000..bd2b5a6e5e
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/IntGenerator.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.skywalking.generator;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.common.base.Preconditions;
+import lombok.Data;
+
+@JsonDeserialize(builder = IntGenerator.Builder.class)
+public final class IntGenerator implements Generator<Long> {
+    private final boolean limitedDomain;
+    private final Long min;
+    private final Long max;
+    private final Integer domainSize;
+    private final ThreadLocalRandom random = ThreadLocalRandom.current();
+    private final Set<Long> domain = new HashSet<>();
+
+    public IntGenerator(Builder builder) {
+        min = builder.min;
+        max = builder.max;
+        domainSize = builder.domainSize;
+        limitedDomain = builder.domainSize != null && builder.domainSize > 0;
+
+        reset();
+    }
+
+    @Override
+    public Long next() {
+        if (!limitedDomain) {
+            return next0();
+        }
+        return domain
+            .stream()
+            .skip(random.nextInt(domain.size()))
+            .findFirst()
+            .orElseThrow(() -> new RuntimeException("Should never happen"));
+    }
+
+    @Override
+    public void reset() {
+        if (limitedDomain) {
+            domain.clear();
+
+            while (domain.size() < domainSize) {
+                domain.add(next0());
+            }
+        }
+    }
+
+    private long next0() {
+        if (min != null && max != null) {
+            return random.nextLong(max - min + 1) + min; // Might overflow, but it's not worthy to check
+        }
+        if (min != null) {
+            return Math.abs(random.nextLong()) + min;
+        }
+        return random.nextLong(max);
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(next());
+    }
+
+    @Data
+    public static class Builder {
+        private Long min;
+        private Long max;
+        private Integer domainSize;
+
+        public IntGenerator build() {
+            if (min != null && max != null) {
+                Preconditions.checkArgument(min <= max, "min must be <= max");
+                if (domainSize != null) {
+                    Preconditions.checkArgument(domainSize <= max - min,
+                        "domain size must be <= max - min");
+                }
+            }
+            return new IntGenerator(this);
+        }
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/ListGenerator.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/ListGenerator.java
new file mode 100644
index 0000000000..07974cd3c7
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/ListGenerator.java
@@ -0,0 +1,60 @@
+/*
+ * 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.skywalking.generator;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import lombok.Data;
+
+@JsonDeserialize(builder = ListGenerator.Builder.class)
+public final class ListGenerator<T> implements Generator<List<T>> {
+    private final T item;
+    private final int size;
+
+    public ListGenerator(Builder<T> builder) {
+        item = builder.item;
+        size = builder.size;
+    }
+
+    @Override
+    public List<T> next() {
+        return IntStream
+            .range(0, size)
+            .mapToObj($ -> item)
+            .collect(Collectors.toList());
+    }
+
+    @Override
+    public void reset() {
+        ((Generator<?>) item).reset();
+    }
+
+    @Data
+    public static class Builder<T> {
+        private int size;
+        private T item;
+
+        public ListGenerator<T> build() {
+            return new ListGenerator<>(this);
+        }
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/SequenceGenerator.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/SequenceGenerator.java
new file mode 100644
index 0000000000..3df3f9246a
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/SequenceGenerator.java
@@ -0,0 +1,118 @@
+/*
+ * 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.skywalking.generator;
+
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.common.base.Preconditions;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@JsonDeserialize(builder = SequenceGenerator.Builder.class)
+public final class SequenceGenerator implements Generator<Long> {
+    private final boolean limitedDomain;
+    private final long min;
+    private final long max;
+    private final long step;
+    private final Integer fluctuation;
+    private final Integer domainSize;
+    private final Random random = ThreadLocalRandom.current();
+    private final Set<Long> domain = new HashSet<>();
+    private volatile Long last;
+
+    public SequenceGenerator(Builder builder) {
+        min = builder.min;
+        max = builder.max;
+        step = builder.step;
+        fluctuation = builder.fluctuation;
+        domainSize = builder.domainSize;
+        limitedDomain = builder.domainSize != null && builder.domainSize > 0;
+
+        reset();
+    }
+
+    @Override
+    public Long next() {
+        if (!limitedDomain) {
+            return next0();
+        }
+        return domain
+            .stream()
+            .skip(random.nextInt(domain.size()))
+            .findFirst()
+            .orElseThrow(() -> new RuntimeException("Should never happen"));
+    }
+
+    private synchronized long next0() {
+        long next = last == null ? min : last + step;
+
+        if (fluctuation != null) {
+            int j = random.nextInt(fluctuation);
+            next += random.nextBoolean() ? j : -j;
+        }
+
+        if (next > max) {
+            return max;
+        }
+        if (next < min) {
+            return min;
+        }
+
+        return last = next;
+    }
+
+    @Override
+    public void reset() {
+        last = null;
+        if (limitedDomain) {
+            domain.clear();
+            while (domain.size() < domainSize) {
+                domain.add(next0());
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(next());
+    }
+
+    @Data
+    @Accessors(chain = true)
+    public static class Builder {
+        private long min = Long.MIN_VALUE;
+        private long max = Long.MAX_VALUE;
+        private long step = 1;
+        private Integer domainSize;
+        private Integer fluctuation;
+
+        public SequenceGenerator build() {
+            if (domainSize != null) {
+                Preconditions.checkArgument(domainSize > 0, "domainSize must be > 0");
+                Preconditions.checkArgument(domainSize + min <= max,
+                    "domain size must be <= max - min");
+            }
+            return new SequenceGenerator(this);
+        }
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/StringGenerator.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/StringGenerator.java
new file mode 100644
index 0000000000..06a6bfb770
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/StringGenerator.java
@@ -0,0 +1,90 @@
+/*
+ * 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.skywalking.generator;
+
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.common.base.Strings;
+import org.apache.commons.lang3.RandomStringUtils;
+import lombok.Data;
+
+@JsonDeserialize(builder = StringGenerator.Builder.class)
+public final class StringGenerator implements Generator<String> {
+    private final int length;
+    private final String prefix;
+    private final boolean letters;
+    private final boolean numbers;
+    private final boolean limitedDomain;
+    private final Random random = ThreadLocalRandom.current();
+    private final Set<String> domain = new HashSet<>();
+
+    public StringGenerator(Builder builder) {
+        length = builder.length;
+        prefix = builder.prefix;
+        letters = builder.letters;
+        numbers = builder.numbers;
+        limitedDomain = builder.domainSize > 0;
+
+        if (limitedDomain) {
+            while (domain.size() < builder.domainSize) {
+                final String r = RandomStringUtils.random(length, letters, numbers);
+                if (!Strings.isNullOrEmpty(builder.prefix)) {
+                    domain.add(builder.prefix + r);
+                } else {
+                    domain.add(r);
+                }
+            }
+        }
+    }
+
+    @Override
+    public String next() {
+        if (!limitedDomain) {
+            return Strings.nullToEmpty(prefix)
+                + RandomStringUtils.random(length, letters, numbers);
+        }
+        return domain
+            .stream()
+            .skip(random.nextInt(domain.size()))
+            .findFirst()
+            .orElseThrow(() -> new RuntimeException("Should never happen"));
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(next());
+    }
+
+    @Data
+    public static class Builder {
+        private int length;
+        private String prefix;
+        private boolean letters;
+        private boolean numbers;
+        private int domainSize;
+
+        public StringGenerator build() {
+            return new StringGenerator(this);
+        }
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/UUIDGenerator.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/UUIDGenerator.java
new file mode 100644
index 0000000000..22a75efe8b
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/generator/UUIDGenerator.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.skywalking.generator;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import lombok.Data;
+
+@JsonDeserialize(builder = UUIDGenerator.Builder.class)
+public final class UUIDGenerator implements Generator<String> {
+    private final int changingFrequency;
+    private final AtomicInteger counter;
+    private final AtomicReference<String> last =
+        new AtomicReference<>(UUID.randomUUID().toString());
+
+    public UUIDGenerator(Builder builder) {
+        checkArgument(builder.changingFrequency > 0, "changingFrequency must be greater than 0");
+        changingFrequency = builder.changingFrequency;
+        counter = new AtomicInteger();
+        reset();
+    }
+
+    @Override
+    public String next() {
+        if (counter.incrementAndGet() < changingFrequency) {
+            return last.get();
+        }
+        reset();
+        return last.getAndSet(UUID.randomUUID().toString());
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(next());
+    }
+
+    @Override
+    public void reset() {
+        counter.set(0);
+    }
+
+    @Data
+    public static class Builder {
+        private int changingFrequency = 1;
+
+        public UUIDGenerator build() {
+            return new UUIDGenerator(this);
+        }
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/module/DataGeneratorModule.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/module/DataGeneratorModule.java
new file mode 100644
index 0000000000..044f91e5df
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/module/DataGeneratorModule.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.skywalking.module;
+
+import org.apache.skywalking.oap.server.library.module.ModuleDefine;
+
+public class DataGeneratorModule extends ModuleDefine {
+    public static final String NAME = "data-generator";
+
+    public DataGeneratorModule() {
+        super(NAME);
+    }
+
+    @Override
+    public Class<?>[] services() {
+        return new Class[0];
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/module/DataGeneratorModuleProvider.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/module/DataGeneratorModuleProvider.java
new file mode 100644
index 0000000000..23ce2a6fa3
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/module/DataGeneratorModuleProvider.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.skywalking.module;
+
+import java.util.Arrays;
+import java.util.List;
+import com.linecorp.armeria.common.HttpMethod;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.server.HTTPHandlerRegister;
+import org.apache.skywalking.oap.server.library.module.ModuleConfig;
+import org.apache.skywalking.oap.server.library.module.ModuleDefine;
+import org.apache.skywalking.oap.server.library.module.ModuleProvider;
+import org.apache.skywalking.oap.server.library.module.ModuleStartException;
+import org.apache.skywalking.oap.server.library.module.ServiceNotProvidedException;
+import org.apache.skywalking.restapi.LogGeneratorHandler;
+import org.apache.skywalking.restapi.SegmentGeneratorHandler;
+
+public class DataGeneratorModuleProvider extends ModuleProvider {
+
+    @Override
+    public String name() {
+        return "default";
+    }
+
+    @Override
+    public Class<? extends ModuleDefine> module() {
+        return DataGeneratorModule.class;
+    }
+
+    @Override
+    public ModuleConfig createConfigBeanIfAbsent() {
+        return null;
+    }
+
+    @Override
+    public void prepare() throws ServiceNotProvidedException, ModuleStartException {
+    }
+
+    @Override
+    public void start() throws ServiceNotProvidedException, ModuleStartException {
+        final List<HttpMethod> methods = Arrays.asList(
+            HttpMethod.GET,
+            HttpMethod.POST,
+            HttpMethod.DELETE
+        );
+        final HTTPHandlerRegister register =
+            getManager()
+                .find(CoreModule.NAME)
+                .provider()
+                .getService(HTTPHandlerRegister.class);
+        register.addHandler(new SegmentGeneratorHandler(getManager()), methods);
+        register.addHandler(new LogGeneratorHandler(getManager()), methods);
+    }
+
+    @Override
+    public void notifyAfterCompleted() throws ServiceNotProvidedException, ModuleStartException {
+    }
+
+    @Override
+    public String[] requiredModules() {
+        return new String[0];
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/LogGeneratorHandler.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/LogGeneratorHandler.java
new file mode 100644
index 0000000000..303bed64f2
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/LogGeneratorHandler.java
@@ -0,0 +1,133 @@
+/*
+ * 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.skywalking.restapi;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.function.IntConsumer;
+import java.util.stream.IntStream;
+import com.linecorp.armeria.common.HttpResponse;
+import com.linecorp.armeria.common.HttpStatus;
+import com.linecorp.armeria.common.MediaType;
+import com.linecorp.armeria.common.util.EventLoopGroups;
+import com.linecorp.armeria.server.annotation.Default;
+import com.linecorp.armeria.server.annotation.Delete;
+import com.linecorp.armeria.server.annotation.Get;
+import com.linecorp.armeria.server.annotation.Param;
+import com.linecorp.armeria.server.annotation.Post;
+import com.linecorp.armeria.server.annotation.ProducesJson;
+import com.linecorp.armeria.server.annotation.RequestObject;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.source.Log;
+import org.apache.skywalking.oap.server.core.source.SourceReceiver;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import io.netty.channel.EventLoopGroup;
+import io.netty.util.concurrent.Future;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class LogGeneratorHandler {
+    private final SourceReceiver sourceReceiver;
+    private final Map<String, Future<?>> futures = new ConcurrentHashMap<>();
+    private final EventLoopGroup eventLoopGroup = EventLoopGroups.newEventLoopGroup(10);
+
+    public LogGeneratorHandler(ModuleManager manager) {
+        sourceReceiver = manager.find(CoreModule.NAME).provider().getService(SourceReceiver.class);
+    }
+
+    @Post("/mock-data/logs/tasks")
+    public HttpResponse generateMockLogs(
+        @Default("0") @Param("size") int size,
+        @Default("0") @Param("qps") int qps,
+        @RequestObject LogRequest request) {
+
+        if (size > 0 && qps > 0) {
+            return HttpResponse.of(
+                HttpStatus.BAD_REQUEST,
+                MediaType.PLAIN_TEXT,
+                "size and qps can't be both set");
+        }
+        log.info("Generate {} mock logs, qps: {}, template: {}", size, qps, request);
+
+        final IntConsumer generator = unused -> {
+            final Log l = request.next();
+            log.debug("Generating log: {}", l);
+            sourceReceiver.receive(l);
+        };
+        final String requestId = UUID.randomUUID().toString();
+        final Future<?> future;
+        if (size > 0) {
+            future = eventLoopGroup.submit(() -> IntStream
+                .range(0, size)
+                .forEach(generator));
+        } else {
+            future = eventLoopGroup.scheduleAtFixedRate(() -> IntStream
+                .range(0, qps)
+                .forEach(generator), 0, 1, TimeUnit.SECONDS);
+        }
+
+        futures.put(requestId, future);
+
+        future.addListener(f -> {
+            if (f.isDone()) {
+                futures.remove(requestId);
+                log.info("Generate mock logs finished: {}, requestId: {}", f.isSuccess(),
+                    requestId);
+            }
+            if (f.cause() != null) {
+                log.error(f.cause().getMessage(), f.cause());
+            }
+        });
+
+        return HttpResponse.of(MediaType.PLAIN_TEXT, requestId);
+    }
+
+    @Delete("/mock-data/logs/task")
+    public HttpResponse cancelRequest(@Param("requestId") String requestId) {
+        final Future<?> future = futures.get(requestId);
+        if (future == null) {
+            return HttpResponse.of(
+                HttpStatus.NOT_FOUND,
+                MediaType.PLAIN_TEXT_UTF_8,
+                "No such request: %s", requestId);
+        }
+        log.info("Cancelling request: {}", requestId);
+        future.cancel(true);
+        return HttpResponse.of(HttpStatus.OK);
+    }
+
+    @Delete("/mock-data/logs/tasks")
+    public HttpResponse cancelAllRequests() {
+        futures.forEach((t, u) -> {
+            log.info("Cancelling request: {}", t);
+            u.cancel(true);
+        });
+        return HttpResponse.of(HttpStatus.OK);
+    }
+
+    @ProducesJson
+    @Get("/mock-data/logs/tasks")
+    public Set<String> listRequest() {
+        return futures.keySet();
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/LogRequest.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/LogRequest.java
new file mode 100644
index 0000000000..bf41e2bf49
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/LogRequest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.skywalking.restapi;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.skywalking.apm.network.common.v3.KeyStringValuePair;
+import org.apache.skywalking.apm.network.logging.v3.LogTags;
+import org.apache.skywalking.generator.Generator;
+import org.apache.skywalking.oap.server.core.analysis.IDManager;
+import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
+import org.apache.skywalking.oap.server.core.analysis.manual.searchtag.Tag;
+import org.apache.skywalking.oap.server.core.query.type.ContentType;
+import org.apache.skywalking.oap.server.core.source.Log;
+import lombok.Data;
+import lombok.SneakyThrows;
+
+@Data
+@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
+public final class LogRequest implements Generator<Log> {
+    @JsonIgnore
+    private final ObjectMapper om = new ObjectMapper();
+
+    private Generator<Long> timestamp;
+    private Generator<String> serviceName;
+    private Generator<String> serviceInstanceName;
+    private Generator<String> endpointName;
+    private Generator<String> traceId;
+    private Generator<String> traceSegmentId;
+    private Generator<Long> spanId;
+    private Generator<Long> contentType;
+    private Generator<String> content;
+    private Generator<List<TagGenerator>> tags;
+    private Generator<Boolean> error;
+
+    @SneakyThrows
+    @Override
+    public Log next() {
+        final Log log = new Log();
+        log.setTimestamp(getTimestamp().next());
+        log.setServiceId(
+            IDManager.ServiceID.buildId(
+                getServiceName().next(),
+                true));
+        log.setServiceInstanceId(
+            IDManager.ServiceInstanceID.buildId(
+                log.getServiceId(),
+                getServiceInstanceName().next()));
+        log.setEndpointId(
+            IDManager.EndpointID.buildId(
+                log.getServiceId(),
+                getEndpointName().next()));
+        log.setTraceId(getTraceId().next());
+        log.setTraceSegmentId(getTraceSegmentId().next());
+        log.setSpanId(getSpanId().next().intValue());
+        log.setContentType(ContentType.instanceOf(getContentType().next().intValue()));
+        log.setContent(getContent().next());
+        log.setError(getError().next());
+        log.setTimeBucket(TimeBucket.getRecordTimeBucket(log.getTimestamp()));
+        log.setTags(
+            getTags()
+                .next()
+                .stream()
+                .map(TagGenerator::next)
+                .collect(Collectors.<Tag>toList()));
+        log.setTagsRawData(
+            LogTags
+                .newBuilder()
+                .addAllData(
+                    log
+                        .getTags()
+                        .stream()
+                        .map(it -> KeyStringValuePair
+                            .newBuilder()
+                            .setKey(it.getKey())
+                            .setValue(it.getValue())
+                            .build())
+                        .collect(Collectors.toList()))
+                .build()
+                .toByteArray());
+        log.setUniqueId(UUID.randomUUID().toString());
+        return log;
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/SegmentGeneratorHandler.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/SegmentGeneratorHandler.java
new file mode 100644
index 0000000000..fb5a0db01e
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/SegmentGeneratorHandler.java
@@ -0,0 +1,134 @@
+/*
+ * 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.skywalking.restapi;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.function.IntConsumer;
+import java.util.stream.IntStream;
+import com.linecorp.armeria.common.HttpResponse;
+import com.linecorp.armeria.common.HttpStatus;
+import com.linecorp.armeria.common.MediaType;
+import com.linecorp.armeria.common.util.EventLoopGroups;
+import com.linecorp.armeria.server.annotation.Default;
+import com.linecorp.armeria.server.annotation.Delete;
+import com.linecorp.armeria.server.annotation.Get;
+import com.linecorp.armeria.server.annotation.Param;
+import com.linecorp.armeria.server.annotation.Post;
+import com.linecorp.armeria.server.annotation.ProducesJson;
+import com.linecorp.armeria.server.annotation.RequestObject;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.source.Segment;
+import org.apache.skywalking.oap.server.core.source.SourceReceiver;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import io.netty.channel.EventLoopGroup;
+import io.netty.util.concurrent.Future;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class SegmentGeneratorHandler {
+    private final SourceReceiver sourceReceiver;
+    private final Map<String, Future<?>> futures = new ConcurrentHashMap<>();
+    private final EventLoopGroup eventLoopGroup = EventLoopGroups.newEventLoopGroup(10);
+
+    public SegmentGeneratorHandler(ModuleManager manager) {
+        sourceReceiver = manager.find(CoreModule.NAME).provider().getService(SourceReceiver.class);
+    }
+
+    @Post("/mock-data/segments/tasks")
+    public HttpResponse generateMockSegments(
+        @Default("0") @Param("size") int size,
+        @Default("0") @Param("qps") int qps,
+        @RequestObject SegmentRequest request) {
+
+        if (size > 0 && qps > 0) {
+            return HttpResponse.of(
+                HttpStatus.BAD_REQUEST,
+                MediaType.PLAIN_TEXT,
+                "size and qps can't be both set");
+        }
+        log.info("Generate {} mock segments, qps: {}, template: {}", size, qps, request);
+
+        final IntConsumer generator = unused -> {
+            final Segment segment = request.next();
+            log.debug("Generating segment: {}", segment);
+            sourceReceiver.receive(segment);
+        };
+        final String requestId = UUID.randomUUID().toString();
+        final Future<?> future;
+        if (size > 0) {
+            future = eventLoopGroup.submit(() -> IntStream
+                .range(0, size)
+                .forEach(generator));
+        } else {
+            future = eventLoopGroup.scheduleAtFixedRate(() -> IntStream
+                .range(0, qps)
+                .forEach(generator), 0, 1, TimeUnit.SECONDS);
+        }
+
+        futures.put(requestId, future);
+
+        future.addListener(f -> {
+            if (f.isDone()) {
+                futures.remove(requestId);
+                log.info("Generate mock segments finished: {}, requestId: {}", f.isSuccess(),
+                    requestId);
+            }
+            if (f.cause() != null && !(f.cause() instanceof CancellationException)) {
+                log.error("Exception in future: ", f.cause());
+            }
+        });
+
+        return HttpResponse.of(MediaType.PLAIN_TEXT, requestId);
+    }
+
+    @Delete("/mock-data/segments/task")
+    public HttpResponse cancelRequest(@Param("requestId") String requestId) {
+        final Future<?> future = futures.get(requestId);
+        if (future == null) {
+            return HttpResponse.of(
+                HttpStatus.NOT_FOUND,
+                MediaType.PLAIN_TEXT_UTF_8,
+                "No such request: %s", requestId);
+        }
+        log.info("Cancelling request: {}", requestId);
+        future.cancel(true);
+        return HttpResponse.of(HttpStatus.OK);
+    }
+
+    @Delete("/mock-data/segments/tasks")
+    public HttpResponse cancelAllRequests() {
+        futures.forEach((t, u) -> {
+            log.info("Cancelling request: {}", t);
+            u.cancel(true);
+        });
+        return HttpResponse.of(HttpStatus.OK);
+    }
+
+    @ProducesJson
+    @Get("/mock-data/segments/tasks")
+    public Set<String> listRequest() {
+        return futures.keySet();
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/SegmentRequest.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/SegmentRequest.java
new file mode 100644
index 0000000000..70d4d63167
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/SegmentRequest.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.skywalking.restapi;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import org.apache.skywalking.apm.network.language.agent.v3.SegmentObject;
+import org.apache.skywalking.apm.network.language.agent.v3.SpanObject;
+import org.apache.skywalking.generator.Generator;
+import org.apache.skywalking.oap.server.core.analysis.IDManager;
+import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
+import org.apache.skywalking.oap.server.core.analysis.manual.searchtag.Tag;
+import org.apache.skywalking.oap.server.core.source.Segment;
+import lombok.Data;
+
+@Data
+@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
+public final class SegmentRequest implements Generator<Segment> {
+    private Generator<String> segmentId;
+    private Generator<String> traceId;
+    private Generator<String> serviceName;
+    private Generator<String> serviceInstanceName;
+    private Generator<String> endpointName;
+    private Generator<Long> startTime;
+    private Generator<Long> latency;
+    private Generator<Long> error;
+    private Generator<List<TagGenerator>> tags;
+    private Generator<List<SpanGenerator>> spans;
+
+    @Override
+    public Segment next() {
+        final String traceId = getTraceId().next();
+        final String serviceName = getServiceName().next();
+        final String serviceInstanceName = getServiceInstanceName().next();
+        final String endpointName = getEndpointName().next();
+        final String segmentId = getSegmentId().next();
+
+        final SegmentObject segmentObj = SegmentObject
+            .newBuilder()
+            .setTraceId(traceId)
+            .setTraceSegmentId(segmentId)
+            .addAllSpans(
+                getSpans()
+                    .next()
+                    .stream()
+                    .map(SpanGenerator::next)
+                    .collect(Collectors.<SpanObject>toList()))
+            .setService(serviceName)
+            .setServiceInstance(serviceInstanceName)
+            .build();
+
+        // Reset the span generator to generate the span id from 0
+        getSpans().reset();
+
+        final Segment segment = new Segment();
+        segment.setSegmentId(segmentId);
+        segment.setTraceId(traceId);
+        segment.setServiceId(
+            IDManager.ServiceID.buildId(serviceName, true));
+        segment.setServiceInstanceId(
+            IDManager.ServiceInstanceID.buildId(
+                segment.getServiceId(),
+                serviceInstanceName));
+        segment.setEndpointId(
+            IDManager.EndpointID.buildId(
+                segment.getServiceId(),
+                endpointName));
+        segment.setStartTime(getStartTime().next());
+        segment.setLatency(getLatency().next().intValue());
+        segment.setIsError(getError().next().intValue());
+        segment.setTimeBucket(TimeBucket.getRecordTimeBucket(segment.getStartTime()));
+        segment.setTags(
+            getTags()
+                .next()
+                .stream()
+                .map(TagGenerator::next)
+                .collect(Collectors.<Tag>toList()));
+
+        segment.setDataBinary(segmentObj.toByteArray());
+        return segment;
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/SpanGenerator.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/SpanGenerator.java
new file mode 100644
index 0000000000..b8af9aea77
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/SpanGenerator.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.skywalking.restapi;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import org.apache.skywalking.apm.network.common.v3.KeyStringValuePair;
+import org.apache.skywalking.apm.network.language.agent.v3.SpanLayer;
+import org.apache.skywalking.apm.network.language.agent.v3.SpanObject;
+import org.apache.skywalking.generator.Generator;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
+public final class SpanGenerator implements Generator<SpanObject> {
+    private Generator<Long> spanId;
+    private Generator<Long> parentSpanId;
+    private Generator<Long> startTime;
+    private Generator<Long> endTime;
+    private Generator<String> operationName;
+    private Generator<String> peer;
+    private Generator<Long> spanLayer;
+    private Generator<Long> componentId;
+    private Generator<Boolean> error;
+    private Generator<List<TagGenerator>> tags;
+
+    @Override
+    public SpanObject next() {
+        return SpanObject
+            .newBuilder()
+            .setSpanId(getSpanId().next().intValue())
+            .setParentSpanId(getParentSpanId().next().intValue())
+            .setStartTime(getStartTime().next())
+            .setEndTime(getEndTime().next())
+            .setOperationName(getOperationName().next())
+            .setPeer(getPeer().next())
+            .setSpanLayer(SpanLayer.forNumber(getSpanLayer().next().intValue()))
+            .setComponentId(getComponentId().next().intValue())
+            .setIsError(getError().next())
+            .addAllTags(
+                getTags()
+                    .next()
+                    .stream()
+                    .map(TagGenerator::next)
+                    .map(it -> KeyStringValuePair
+                        .newBuilder().setKey(it.getKey())
+                        .setValue(it.getValue()).build())
+                    .collect(Collectors.toList()))
+            .build();
+    }
+
+    @Override
+    public void reset() {
+        spanId.reset();
+        parentSpanId.reset();
+        startTime.reset();
+        endTime.reset();
+        operationName.reset();
+        peer.reset();
+        spanLayer.reset();
+        componentId.reset();
+        error.reset();
+        tags.reset();
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/TagGenerator.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/TagGenerator.java
new file mode 100644
index 0000000000..e55d79ba2d
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/restapi/TagGenerator.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.skywalking.restapi;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import org.apache.skywalking.generator.Generator;
+import org.apache.skywalking.oap.server.core.analysis.manual.searchtag.Tag;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
+public final class TagGenerator implements Generator<Tag> {
+    private Generator<String> key;
+    private Generator<String> value;
+
+    @Override
+    public Tag next() {
+        return new Tag(key.next(), value.next());
+    }
+
+    @Override
+    public void reset() {
+        key.reset();
+        value.reset();
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/starter/DataGeneratorStartUp.java b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/starter/DataGeneratorStartUp.java
new file mode 100644
index 0000000000..4f3581137e
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/java/org/apache/skywalking/starter/DataGeneratorStartUp.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.skywalking.starter;
+
+import org.apache.skywalking.oap.server.starter.OAPServerBootstrap;
+
+public class DataGeneratorStartUp {
+    public static void main(String[] args) {
+        OAPServerBootstrap.start();
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine b/oap-server/server-tools/data-generator/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
new file mode 100644
index 0000000000..02226bb7fb
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
@@ -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.apache.skywalking.module.DataGeneratorModule
diff --git a/oap-server/server-tools/data-generator/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider b/oap-server/server-tools/data-generator/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
new file mode 100644
index 0000000000..01a75a2a5a
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
@@ -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.apache.skywalking.module.DataGeneratorModuleProvider
diff --git a/oap-server/server-tools/data-generator/src/main/resources/application.yml b/oap-server/server-tools/data-generator/src/main/resources/application.yml
new file mode 100755
index 0000000000..60057f1e17
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/main/resources/application.yml
@@ -0,0 +1,179 @@
+# 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.
+
+cluster:
+  selector: ${SW_CLUSTER:standalone}
+  standalone:
+core:
+  selector: ${SW_CORE:default}
+  default:
+    # Mixed: Receive agent data, Level 1 aggregate, Level 2 aggregate
+    # Receiver: Receive agent data, Level 1 aggregate
+    # Aggregator: Level 2 aggregate
+    role: ${SW_CORE_ROLE:Mixed} # Mixed/Receiver/Aggregator
+    restHost: ${SW_CORE_REST_HOST:0.0.0.0}
+    restPort: ${SW_CORE_REST_PORT:12800}
+    restContextPath: ${SW_CORE_REST_CONTEXT_PATH:/}
+    restMinThreads: ${SW_CORE_REST_JETTY_MIN_THREADS:1}
+    restMaxThreads: ${SW_CORE_REST_JETTY_MAX_THREADS:200}
+    restIdleTimeOut: ${SW_CORE_REST_JETTY_IDLE_TIMEOUT:30000}
+    restAcceptorPriorityDelta: ${SW_CORE_REST_JETTY_DELTA:0}
+    restAcceptQueueSize: ${SW_CORE_REST_JETTY_QUEUE_SIZE:0}
+    httpMaxRequestHeaderSize: ${SW_CORE_HTTP_MAX_REQUEST_HEADER_SIZE:8192}
+    gRPCHost: ${SW_CORE_GRPC_HOST:0.0.0.0}
+    gRPCPort: ${SW_CORE_GRPC_PORT:11800}
+    maxConcurrentCallsPerConnection: ${SW_CORE_GRPC_MAX_CONCURRENT_CALL:0}
+    maxMessageSize: ${SW_CORE_GRPC_MAX_MESSAGE_SIZE:0}
+    gRPCThreadPoolQueueSize: ${SW_CORE_GRPC_POOL_QUEUE_SIZE:-1}
+    gRPCThreadPoolSize: ${SW_CORE_GRPC_THREAD_POOL_SIZE:-1}
+    gRPCSslEnabled: ${SW_CORE_GRPC_SSL_ENABLED:false}
+    gRPCSslKeyPath: ${SW_CORE_GRPC_SSL_KEY_PATH:""}
+    gRPCSslCertChainPath: ${SW_CORE_GRPC_SSL_CERT_CHAIN_PATH:""}
+    gRPCSslTrustedCAPath: ${SW_CORE_GRPC_SSL_TRUSTED_CA_PATH:""}
+    downsampling:
+      - Hour
+      - Day
+    # Set a timeout on metrics data. After the timeout has expired, the metrics data will automatically be deleted.
+    enableDataKeeperExecutor: ${SW_CORE_ENABLE_DATA_KEEPER_EXECUTOR:true} # Turn it off then automatically metrics data delete will be close.
+    dataKeeperExecutePeriod: ${SW_CORE_DATA_KEEPER_EXECUTE_PERIOD:5} # How often the data keeper executor runs periodically, unit is minute
+    recordDataTTL: ${SW_CORE_RECORD_DATA_TTL:3} # Unit is day
+    metricsDataTTL: ${SW_CORE_METRICS_DATA_TTL:7} # Unit is day
+    # The period of L1 aggregation flush to L2 aggregation. Unit is ms.
+    l1FlushPeriod: ${SW_CORE_L1_AGGREGATION_FLUSH_PERIOD:500}
+    # The threshold of session time. Unit is ms. Default value is 70s.
+    storageSessionTimeout: ${SW_CORE_STORAGE_SESSION_TIMEOUT:70000}
+    # The period of doing data persistence. Unit is second.Default value is 25s
+    persistentPeriod: ${SW_CORE_PERSISTENT_PERIOD:25}
+    # Cache metrics data for 1 minute to reduce database queries, and if the OAP cluster changes within that minute,
+    # the metrics may not be accurate within that minute.
+    enableDatabaseSession: ${SW_CORE_ENABLE_DATABASE_SESSION:true}
+    topNReportPeriod: ${SW_CORE_TOPN_REPORT_PERIOD:10} # top_n record worker report cycle, unit is minute
+    # Extra model column are the column defined by in the codes, These columns of model are not required logically in aggregation or further query,
+    # and it will cause more load for memory, network of OAP and storage.
+    # But, being activated, user could see the name in the storage entities, which make users easier to use 3rd party tool, such as Kibana->ES, to query the data by themselves.
+    activeExtraModelColumns: ${SW_CORE_ACTIVE_EXTRA_MODEL_COLUMNS:false}
+    # The max length of service + instance names should be less than 200
+    serviceNameMaxLength: ${SW_SERVICE_NAME_MAX_LENGTH:70}
+    instanceNameMaxLength: ${SW_INSTANCE_NAME_MAX_LENGTH:70}
+    # The max length of service + endpoint names should be less than 240
+    endpointNameMaxLength: ${SW_ENDPOINT_NAME_MAX_LENGTH:150}
+    # Define the set of span tag keys, which should be searchable through the GraphQL.
+    searchableTracesTags: ${SW_SEARCHABLE_TAG_KEYS:http.method,status_code,db.type,db.instance,mq.queue,mq.topic,mq.broker}
+    # Define the set of log tag keys, which should be searchable through the GraphQL.
+    searchableLogsTags: ${SW_SEARCHABLE_LOGS_TAG_KEYS:level}
+    # Define the set of alarm tag keys, which should be searchable through the GraphQL.
+    searchableAlarmTags: ${SW_SEARCHABLE_ALARM_TAG_KEYS:level}
+    # The number of threads used to prepare metrics data to the storage.
+    prepareThreads: ${SW_CORE_PREPARE_THREADS:2}
+    # Turn it on then automatically grouping endpoint by the given OpenAPI definitions.
+    enableEndpointNameGroupingByOpenapi: ${SW_CORE_ENABLE_ENDPOINT_NAME_GROUPING_BY_OPAENAPI:true}
+storage:
+  selector: ${SW_STORAGE:h2}
+  elasticsearch:
+    namespace: ${SW_NAMESPACE:""}
+    clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:localhost:9200}
+    protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:"http"}
+    connectTimeout: ${SW_STORAGE_ES_CONNECT_TIMEOUT:3000}
+    socketTimeout: ${SW_STORAGE_ES_SOCKET_TIMEOUT:30000}
+    responseTimeout: ${SW_STORAGE_ES_RESPONSE_TIMEOUT:15000}
+    numHttpClientThread: ${SW_STORAGE_ES_NUM_HTTP_CLIENT_THREAD:0}
+    user: ${SW_ES_USER:""}
+    password: ${SW_ES_PASSWORD:""}
+    trustStorePath: ${SW_STORAGE_ES_SSL_JKS_PATH:""}
+    trustStorePass: ${SW_STORAGE_ES_SSL_JKS_PASS:""}
+    secretsManagementFile: ${SW_ES_SECRETS_MANAGEMENT_FILE:""} # Secrets management file in the properties format includes the username, password, which are managed by 3rd party tool.
+    dayStep: ${SW_STORAGE_DAY_STEP:1} # Represent the number of days in the one minute/hour/day index.
+    indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:1} # Shard number of new indexes
+    indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:1} # Replicas number of new indexes
+    # Super data set has been defined in the codes, such as trace segments.The following 3 config would be improve es performance when storage super size data in es.
+    superDatasetDayStep: ${SW_SUPERDATASET_STORAGE_DAY_STEP:-1} # Represent the number of days in the super size dataset record index, the default value is the same as dayStep when the value is less than 0
+    superDatasetIndexShardsFactor: ${SW_STORAGE_ES_SUPER_DATASET_INDEX_SHARDS_FACTOR:5} #  This factor provides more shards for the super data set, shards number = indexShardsNumber * superDatasetIndexShardsFactor. Also, this factor effects Zipkin and Jaeger traces.
+    superDatasetIndexReplicasNumber: ${SW_STORAGE_ES_SUPER_DATASET_INDEX_REPLICAS_NUMBER:0} # Represent the replicas number in the super size dataset record index, the default value is 0.
+    indexTemplateOrder: ${SW_STORAGE_ES_INDEX_TEMPLATE_ORDER:0} # the order of index template
+    bulkActions: ${SW_STORAGE_ES_BULK_ACTIONS:5000} # Execute the async bulk record data every ${SW_STORAGE_ES_BULK_ACTIONS} requests
+    # flush the bulk every 10 seconds whatever the number of requests
+    # INT(flushInterval * 2/3) would be used for index refresh period.
+    flushInterval: ${SW_STORAGE_ES_FLUSH_INTERVAL:15}
+    concurrentRequests: ${SW_STORAGE_ES_CONCURRENT_REQUESTS:2} # the number of concurrent requests
+    resultWindowMaxSize: ${SW_STORAGE_ES_QUERY_MAX_WINDOW_SIZE:10000}
+    metadataQueryMaxSize: ${SW_STORAGE_ES_QUERY_MAX_SIZE:5000}
+    segmentQueryMaxSize: ${SW_STORAGE_ES_QUERY_SEGMENT_SIZE:200}
+    profileTaskQueryMaxSize: ${SW_STORAGE_ES_QUERY_PROFILE_TASK_SIZE:200}
+    oapAnalyzer: ${SW_STORAGE_ES_OAP_ANALYZER:"{\"analyzer\":{\"oap_analyzer\":{\"type\":\"stop\"}}}"} # the oap analyzer.
+    oapLogAnalyzer: ${SW_STORAGE_ES_OAP_LOG_ANALYZER:"{\"analyzer\":{\"oap_log_analyzer\":{\"type\":\"standard\"}}}"} # the oap log analyzer. It could be customized by the ES analyzer configuration to support more language log formats, such as Chinese log, Japanese log and etc.
+    advanced: ${SW_STORAGE_ES_ADVANCED:""}
+  h2:
+    driver: ${SW_STORAGE_H2_DRIVER:org.h2.jdbcx.JdbcDataSource}
+    url: ${SW_STORAGE_H2_URL:jdbc:h2:mem:skywalking-oap-db;DB_CLOSE_DELAY=-1}
+    user: ${SW_STORAGE_H2_USER:sa}
+    metadataQueryMaxSize: ${SW_STORAGE_H2_QUERY_MAX_SIZE:5000}
+    maxSizeOfArrayColumn: ${SW_STORAGE_MAX_SIZE_OF_ARRAY_COLUMN:20}
+    numOfSearchableValuesPerTag: ${SW_STORAGE_NUM_OF_SEARCHABLE_VALUES_PER_TAG:2}
+    maxSizeOfBatchSql: ${SW_STORAGE_MAX_SIZE_OF_BATCH_SQL:100}
+    asyncBatchPersistentPoolSize: ${SW_STORAGE_ASYNC_BATCH_PERSISTENT_POOL_SIZE:1}
+
+agent-analyzer:
+  selector: ${SW_AGENT_ANALYZER:default}
+  default:
+    # The default sampling rate and the default trace latency time configured by the 'traceSamplingPolicySettingsFile' file.
+    traceSamplingPolicySettingsFile: ${SW_TRACE_SAMPLING_POLICY_SETTINGS_FILE:trace-sampling-policy-settings.yml}
+    slowDBAccessThreshold: ${SW_SLOW_DB_THRESHOLD:default:200,mongodb:100} # The slow database access thresholds. Unit ms.
+    forceSampleErrorSegment: ${SW_FORCE_SAMPLE_ERROR_SEGMENT:true} # When sampling mechanism active, this config can open(true) force save some error segment. true is default.
+    segmentStatusAnalysisStrategy: ${SW_SEGMENT_STATUS_ANALYSIS_STRATEGY:FROM_SPAN_STATUS} # Determine the final segment status from the status of spans. Available values are `FROM_SPAN_STATUS` , `FROM_ENTRY_SPAN` and `FROM_FIRST_SPAN`. `FROM_SPAN_STATUS` represents the segment status would be error if any span is in error status. `FROM_ENTRY_SPAN` means the segment status would be determined by the status of entry spans only. `FROM_FIRST_SPAN` means the segment status would be determine [...]
+    # Nginx and Envoy agents can't get the real remote address.
+    # Exit spans with the component in the list would not generate the client-side instance relation metrics.
+    noUpstreamRealAddressAgents: ${SW_NO_UPSTREAM_REAL_ADDRESS:6000,9000}
+    meterAnalyzerActiveFiles: ${SW_METER_ANALYZER_ACTIVE_FILES:datasource,threadpool} # Which files could be meter analyzed, files split by ","
+
+log-analyzer:
+  selector: ${SW_LOG_ANALYZER:default}
+  default:
+    lalFiles: ${SW_LOG_LAL_FILES:default}
+    malFiles: ${SW_LOG_MAL_FILES:""}
+
+event-analyzer:
+  selector: ${SW_EVENT_ANALYZER:default}
+  default:
+
+query:
+  selector: ${SW_QUERY:graphql}
+  graphql:
+    path: ${SW_QUERY_GRAPHQL_PATH:/graphql}
+    # Enable the log testing API to test the LAL.
+    # NOTE: This API evaluates untrusted code on the OAP server.
+    # A malicious script can do significant damage (steal keys and secrets, remove files and directories, install malware, etc).
+    # As such, please enable this API only when you completely trust your users.
+    enableLogTestTool: ${SW_QUERY_GRAPHQL_ENABLE_LOG_TEST_TOOL:false}
+
+alarm:
+  selector: ${SW_ALARM:default}
+  default:
+
+telemetry:
+  selector: ${SW_TELEMETRY:none}
+  none:
+
+configuration:
+  selector: ${SW_CONFIGURATION:none}
+  none:
+
+health-checker:
+  selector: ${SW_HEALTH_CHECKER:-}
+  default:
+    checkIntervalSeconds: ${SW_HEALTH_CHECKER_INTERVAL_SECONDS:5}
+
+data-generator:
+  selector: ${SW_DATA_GENERATOR:default}
+  default:
diff --git a/oap-server/server-tools/data-generator/src/test/java/org/apache/skywalking/generator/IntGeneratorTest.java b/oap-server/server-tools/data-generator/src/test/java/org/apache/skywalking/generator/IntGeneratorTest.java
new file mode 100644
index 0000000000..9799f16496
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/test/java/org/apache/skywalking/generator/IntGeneratorTest.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.skywalking.generator;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class IntGeneratorTest {
+    @Test
+    public void testFixedInt() {
+        final IntGenerator.Builder builder = new IntGenerator.Builder();
+        builder.setMin(1L);
+        builder.setMax(1L);
+        final IntGenerator generator = builder.build();
+        for (int i = 0; i < 100; i++) {
+            assertEquals(1, generator.next().intValue());
+        }
+    }
+}
diff --git a/oap-server/server-tools/data-generator/src/test/java/org/apache/skywalking/generator/SequenceGeneratorTest.java b/oap-server/server-tools/data-generator/src/test/java/org/apache/skywalking/generator/SequenceGeneratorTest.java
new file mode 100644
index 0000000000..af624cbe14
--- /dev/null
+++ b/oap-server/server-tools/data-generator/src/test/java/org/apache/skywalking/generator/SequenceGeneratorTest.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.skywalking.generator;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+public final class SequenceGeneratorTest {
+    @Test
+    public void testSequence() {
+        SequenceGenerator generator =
+            new SequenceGenerator.Builder()
+                .setMin(1L)
+                .setMax(100L)
+                .build();
+
+        for (int i = 0; i < 10; i++) {
+            assertEquals(i + 1, generator.next().intValue());
+        }
+    }
+
+    @Test
+    public void testFluctuation() {
+        SequenceGenerator generator =
+            new SequenceGenerator.Builder()
+                .setMin(1L)
+                .setMax(100L)
+                .setFluctuation(1)
+                .build();
+
+        for (int i = 1; i < 10; i++) {
+            Long next = generator.next();
+            assertTrue(i <= next.intValue());
+            assertTrue(i * 2 >= next.intValue());
+        }
+    }
+}
diff --git a/oap-server/server-tools/pom.xml b/oap-server/server-tools/pom.xml
index 4371350c79..0ed3da6d8b 100644
--- a/oap-server/server-tools/pom.xml
+++ b/oap-server/server-tools/pom.xml
@@ -30,5 +30,6 @@
 
     <modules>
         <module>profile-exporter</module>
+        <module>data-generator</module>
     </modules>
 </project>