You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ke...@apache.org on 2021/02/03 15:38:25 UTC

[skywalking] branch events updated (fdd6d7b -> 4357db1)

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

kezhenxu94 pushed a change to branch events
in repository https://gitbox.apache.org/repos/asf/skywalking.git.


    omit fdd6d7b  Add a new concept "Event" and its implementations to collect events
     new 4357db1  Add a new concept "Event" and its implementations to collect events

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (fdd6d7b)
            \
             N -- N -- N   refs/heads/events (4357db1)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 README.md                                                             | 2 +-
 docs/en/concepts-and-designs/event.md                                 | 2 +-
 .../skywalking/oap/server/analyzer/event/EventAnalyzerModule.java     | 2 +-
 .../server/analyzer/event/listener/EventRecordAnalyzerListener.java   | 2 ++
 oap-server/server-bootstrap/src/main/resources/application.yml        | 4 ++++
 .../main/java/org/apache/skywalking/oap/server/core/event/Event.java  | 2 ++
 .../oap/server/receiver/event/grpc/EventGrpcServiceHandler.java       | 3 ++-
 7 files changed, 13 insertions(+), 4 deletions(-)


[skywalking] 01/01: Add a new concept "Event" and its implementations to collect events

Posted by ke...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 4357db1a5e6b12f90217b4c441c581eb064314f8
Author: kezhenxu94 <ke...@apache.org>
AuthorDate: Tue Jan 12 16:17:14 2021 +0800

    Add a new concept "Event" and its implementations to collect events
---
 .github/workflows/ci-it.yaml                       |   5 +-
 .github/workflows/e2e.event.yaml                   |  53 ++++++
 CHANGES.md                                         |   2 +-
 README.md                                          |   2 +-
 docs/en/concepts-and-designs/README.md             |   2 +
 docs/en/concepts-and-designs/event.md              |  56 ++++++
 docs/en/protocols/README.md                        |   3 +
 docs/en/setup/backend/backend-receivers.md         |   8 +-
 docs/en/setup/backend/configuration-vocabulary.md  |   1 +
 oap-server/analyzer/{ => event-analyzer}/pom.xml   |  26 +--
 .../oap/server/analyzer/event/EventAnalyzer.java   |  60 ++++++
 .../analyzer/event/EventAnalyzerModule.java}       |  21 +-
 .../analyzer/event/EventAnalyzerModuleConfig.java} |  13 +-
 .../event/EventAnalyzerModuleProvider.java         |  70 +++++++
 .../analyzer/event/EventAnalyzerService.java}      |  15 +-
 .../analyzer/event/EventAnalyzerServiceImpl.java   |  50 +++++
 .../event/listener/EventAnalyzerListener.java}     |  26 ++-
 .../EventAnalyzerListenerFactoryManager.java}      |  16 +-
 .../listener/EventRecordAnalyzerListener.java      |  81 ++++++++
 ...ywalking.oap.server.library.module.ModuleDefine |  19 ++
 ...alking.oap.server.library.module.ModuleProvider |  19 ++
 oap-server/analyzer/pom.xml                        |   3 +-
 oap-server/server-bootstrap/pom.xml                |   5 +
 .../src/main/resources/application.yml             |   8 +
 .../server-bootstrap/src/main/resources/log4j2.xml |   1 +
 .../skywalking/oap/server/core/CoreModule.java     |   2 +
 .../oap/server/core/CoreModuleProvider.java        |   2 +
 .../skywalking/oap/server/core/event/Event.java    | 212 +++++++++++++++++++++
 .../oap/server/core/query/EventQueryService.java   |  59 ++++++
 .../oap/server/core/query/type/event/Event.java    |  62 ++++++
 .../query/type/event/EventQueryCondition.java}     |  32 +++-
 .../server/core/query/type/event/EventType.java}   |  20 +-
 .../oap/server/core/query/type/event/Events.java}  |  17 +-
 .../oap/server/core/query/type/event/Source.java}  |  21 +-
 .../oap/server/core/source/DefaultScopeDefine.java |   4 +-
 .../oap/server/core/storage/StorageModule.java     |   4 +-
 .../server/core/storage/query/IEventQueryDAO.java} |  17 +-
 .../skywalking/oap/server/core/CoreModuleTest.java |   2 +-
 oap-server/server-library/library-server/pom.xml   |   2 +-
 .../oap/query/graphql/GraphQLQueryProvider.java    |   3 +
 .../oap/query/graphql/resolver/EventQuery.java     |  51 +++++
 oap-server/server-receiver-plugin/pom.xml          |   3 +-
 .../skywalking-event-receiver-plugin}/pom.xml      |  23 +--
 .../oap/server/receiver/event/EventModule.java}    |  19 +-
 .../server/receiver/event/EventModuleConfig.java}  |  13 +-
 .../server/receiver/event/EventModuleProvider.java |  75 ++++++++
 .../event/grpc/EventGrpcServiceHandler.java        |  90 +++++++++
 ...ywalking.oap.server.library.module.ModuleDefine |  19 ++
 ...alking.oap.server.library.module.ModuleProvider |  19 ++
 .../StorageModuleElasticsearchProvider.java        |   4 +
 .../elasticsearch/query/ESEventQueryDAO.java       | 140 ++++++++++++++
 .../StorageModuleElasticsearch7Provider.java       |   4 +
 .../elasticsearch7/query/ES7EventQueryDAO.java     |  50 +++++
 .../plugin/influxdb/InfluxStorageProvider.java     |   4 +
 .../plugin/influxdb/query/EventQueryDAO.java       | 164 ++++++++++++++++
 .../storage/plugin/jdbc/h2/H2StorageProvider.java  |   4 +
 .../plugin/jdbc/h2/dao/H2EventQueryDAO.java        | 142 ++++++++++++++
 .../plugin/jdbc/mysql/MySQLStorageProvider.java    |   4 +
 .../plugin/jdbc/tidb/TiDBStorageProvider.java      |   4 +
 .../tool/profile/core/MockCoreModuleProvider.java  |   3 +
 .../apache/skywalking/e2e/SimpleQueryClient.java   |  21 ++
 .../skywalking/e2e/common/KeyValueMatcher.java     |   2 +-
 .../KeyValueMatcher.java => event/Event.java}      |  61 +++---
 .../org/apache/skywalking/e2e/event/EventData.java |  24 +--
 .../apache/skywalking/e2e/event/EventMatcher.java  | 112 +++++++++++
 .../apache/skywalking/e2e/event/EventsMatcher.java |  51 +++++
 .../apache/skywalking/e2e/event/EventsQuery.java   |  23 ++-
 test/e2e/e2e-data/src/main/resources/events.gql    |  39 ++++
 test/e2e/e2e-test/docker/Dockerfile.cli            |  33 ++++
 .../e2e-test/docker/event/docker-compose.es6.yml   |  47 +++++
 .../e2e-test/docker/event/docker-compose.es7.0.yml |  47 +++++
 .../e2e-test/docker/event/docker-compose.h2.yml    |  45 +++++
 .../docker/event/docker-compose.influxdb.yml       |  44 +++++
 .../e2e-test/docker/event/docker-compose.mysql.yml |  49 +++++
 test/e2e/e2e-test/docker/event/docker-compose.yml  |  56 ++++++
 .../org/apache/skywalking/e2e/event/EventE2E.java  |  66 +++++++
 .../src/test/resources/expected/event/events.yml   |  30 +++
 77 files changed, 2403 insertions(+), 206 deletions(-)

diff --git a/.github/workflows/ci-it.yaml b/.github/workflows/ci-it.yaml
index c9636ef..db1700d 100644
--- a/.github/workflows/ci-it.yaml
+++ b/.github/workflows/ci-it.yaml
@@ -110,4 +110,7 @@ jobs:
         with:
           java-version: 8
       - name: 'Install & Test'
-        run: ./mvnw --batch-mode -P"agent,backend,ui,dist" clean verify install
+        run: |
+          # Given packaging on Mac has a high possibility to fail, we retry one more time here
+          ./mvnw --batch-mode -P"agent,backend,ui,dist" clean verify install || \
+          ./mvnw --batch-mode -P"agent,backend,ui,dist" clean verify install
diff --git a/.github/workflows/e2e.event.yaml b/.github/workflows/e2e.event.yaml
new file mode 100644
index 0000000..ad648c6
--- /dev/null
+++ b/.github/workflows/e2e.event.yaml
@@ -0,0 +1,53 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: E2E
+
+on:
+  pull_request:
+    paths:
+      - '**'
+      - '!**.md'
+  schedule:
+    - cron: '0 18 * * *'
+
+env:
+  SKIP_TEST: true
+
+jobs:
+  Event:
+    name: Event
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        storage: ['h2', 'mysql', 'es6', 'es7.0', 'influxdb']
+    env:
+      SW_STORAGE: ${{ matrix.storage }}
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          submodules: true
+      - name: Cache local Maven repository
+        uses: actions/cache@v2
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+      - name: Run E2E Test
+        uses: ./.github/actions/e2e-test
+        with:
+          test_class: org.apache.skywalking.e2e.event.EventE2E
diff --git a/CHANGES.md b/CHANGES.md
index 313e778..b2f2410 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -14,7 +14,7 @@ Release Notes.
 
 
 #### OAP-Backend
-
+* Add a new concept "Event" and its implementations to collect events.
 
 #### UI
 
diff --git a/README.md b/README.md
index 0b8ed22..7cf9960 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ microservices, cloud native and container-based (Docker, Kubernetes, Mesos) arch
 [![Twitter Follow](https://img.shields.io/twitter/follow/asfskywalking.svg?style=for-the-badge&label=Follow&logo=twitter)](https://twitter.com/AsfSkyWalking)
 
 [![Maven Central](https://img.shields.io/maven-central/v/org.apache.skywalking/apache-skywalking-apm.svg)](http://skywalking.apache.org/downloads/)
-[![CI/IT Tests](https://github.com/apache/skywalking/workflows/CI%20AND%20IT/badge.svg?branch=master)](https://github.com/apache/skywalking/actions?query=branch%3Amaster+event%3Aschedule+workflow%3A%22CI+AND+IT%22)
+[![CI/IT Tests](https://github.com/apache/skywalking/workflows/CI%20AND%20IT/badge.svg?branch=master)](https://github.com/apache/skywalking/actions?query=workflow%3ACI%2BAND%2BIT+event%3Aschedule+branch%3Amaster/)
 [![E2E Tests](https://github.com/apache/skywalking/workflows/E2E/badge.svg?branch=master)](https://github.com/apache/skywalking/actions?query=branch%3Amaster+event%3Aschedule+workflow%3AE2E)
 
 # Abstract
diff --git a/docs/en/concepts-and-designs/README.md b/docs/en/concepts-and-designs/README.md
index f04a7b0..e0a98b1 100644
--- a/docs/en/concepts-and-designs/README.md
+++ b/docs/en/concepts-and-designs/README.md
@@ -23,3 +23,5 @@ SkyWalking already support.
   - [Overview](ui-overview.md). A simple brief about SkyWalking UI.
 - CLI
   - [SkyWalking CLI](https://github.com/apache/skywalking-cli). A command line interface for SkyWalking.
+- Events
+  - [Events](event.md). Introduce the concept of "Event" in SkyWalking.
diff --git a/docs/en/concepts-and-designs/event.md b/docs/en/concepts-and-designs/event.md
new file mode 100644
index 0000000..87c9174
--- /dev/null
+++ b/docs/en/concepts-and-designs/event.md
@@ -0,0 +1,56 @@
+# Events
+
+SkyWalking already supports the three pillars of observability, namely logs, metrics, and traces.
+In reality, a production system experiences many other events that may affect the performance of the system, such as upgrading, rebooting, chaos testing, etc.
+Although some of these events are reflected in the logs, there are many other events that can not. Hence, SkyWalking provides a more native way to collect these events.
+This doc covers the design of how SkyWalking collects events and what events look like in SkyWalking.
+
+## How to Report Events
+
+SkyWalking backend supports three protocols to collect events, gRPC, HTTP, and Kafka. Any agent or CLI that implements one of these protocols can report events to SkyWalking.
+Currently, the officially supported clients to report events are:
+
+- [ ] Java Agent Toolkit: Use the Java agent toolkit to report events from inside the applications.
+- [x] SkyWalking CLI: Use the CLI to report events from the command line interface.
+- [ ] Kubernetes Event Exporter: Deploy an event exporter to refine and report Kubernetes events.
+
+## Event Definition
+
+An event contains the following fields. The definitions of event can be found at the [protocol repo](https://github.com/apache/skywalking-data-collect-protocol/tree/master/event)
+
+### UUID
+
+Unique ID of the event. Because an event may span a long period of time, the UUID is necessary to associate the start time with the end time of the same event. 
+
+### Source
+
+The source object that the event occurs on. In the concepts of SkyWalking, the object is typically service, service instance, etc.
+
+### Name
+
+The name of the event. For example, `Start`, `Stop`, `Crash`, `Reboot`, `Upgrade` etc.
+
+### Type
+
+The type of the event. This field is friendly for UI visualization, where events of type `Normal` are considered as normal operations,
+while `Error` is considered as unexpected operations, such as `Crash` events, therefore we can mark them with different colors to be easier identified.
+
+### Message
+
+The detail of the event that describes why this event happened. This should be a one-line message that briefly describes why the event is reported. Examples of an `Upgrade` event may be something like `Upgrade from ${from_version} to ${to_version}`.
+It's NOT encouraged to include the detailed logs of this event, such as the exception stack trace.
+
+### Parameters
+
+The parameters in the `message` field. This is a simple `<string,string>` map. 
+
+### Start Time
+
+The start time of the event. This field is mandatory when an event occurs.
+
+### End Time
+
+The end time of the event. This field may be empty if the event has not stopped yet, otherwise it should be a valid timestamp after `startTime`.
+
+**NOTE:** When reporting an event, you typically call the report function twice, one for starting of the event and the other one for ending of the event, with the same UUID.
+There are also cases where you have both the start time and end time already, for example, when exporting events from a 3rd-party system, the start time and end time are already known so that you can call the report function only once.
diff --git a/docs/en/protocols/README.md b/docs/en/protocols/README.md
index d7fefaf..87e92f0 100644
--- a/docs/en/protocols/README.md
+++ b/docs/en/protocols/README.md
@@ -50,6 +50,9 @@ the following key info:
 1. Protocol. HTTP, gRPC
 1. DetectPoint. In Service Mesh sidecar, `client` or `server`. In normal L7 proxy, value is `proxy`.
 
+### Events Report Protocol
+
+The protocol is used to report events to the backend. The [doc](../concepts-and-designs/event.md) introduces the definition of an event, and [the protocol repository](https://github.com/apache/skywalking-data-collect-protocol/blob/master/event) defines gRPC services and messages formats of events.
 
 ### 3rd-party instrument protocol
 3rd-party instrument protocols are not defined by SkyWalking. They are just protocols/formats, which SkyWalking is compatible and
diff --git a/docs/en/setup/backend/backend-receivers.md b/docs/en/setup/backend/backend-receivers.md
index b88ed64..36c56e7 100644
--- a/docs/en/setup/backend/backend-receivers.md
+++ b/docs/en/setup/backend/backend-receivers.md
@@ -16,6 +16,7 @@ We have following receivers, and `default` implementors are provided in our Apac
 1. **receiver-browser**. gRPC services to accept browser performance data and error log.
 1. **receiver-log**. gRPC services accept log data.
 1. **configuration-discovery**. gRPC services handle configurationDiscovery.
+1. **receiver-event**. gRPC services to handle events data.
 1. Experimental receivers. All following receivers are in the POC stage, not production ready.
     1. **receiver_zipkin**. See [details](#zipkin-receiver). (Experimental)
     1. **receiver_jaeger**. See [details](#jaeger-receiver). (Experimental)
@@ -72,6 +73,11 @@ receiver-log:
 configuration-discovery:
   selector: ${SW_CONFIGURATION_DISCOVERY:default}
   default:
+
+receiver-event:
+   selector: ${SW_RECEIVER_EVENT:default}
+   default:
+
 ```
 
 ## gRPC/HTTP server for receiver
@@ -207,4 +213,4 @@ receiver_jaeger:
     gRPCPort: ${SW_RECEIVER_JAEGER_PORT:14250}
 ```
 
-NOTICE, Jaeger receiver is only provided in `apache-skywalking-apm-x.y.z.tar.gz` tar.
\ No newline at end of file
+NOTICE, Jaeger receiver is only provided in `apache-skywalking-apm-x.y.z.tar.gz` tar.
diff --git a/docs/en/setup/backend/configuration-vocabulary.md b/docs/en/setup/backend/configuration-vocabulary.md
index 06804fb..2d4dca2 100644
--- a/docs/en/setup/backend/configuration-vocabulary.md
+++ b/docs/en/setup/backend/configuration-vocabulary.md
@@ -261,6 +261,7 @@ core|default|role|Option values, `Mixed/Receiver/Aggregator`. **Receiver** mode
 | - | - | targetPort | The port of target grpc server for receiving export data. | SW_EXPORTER_GRPC_PORT | 9870 |
 | health-checker | default | checkIntervalSeconds | The period of check OAP internal health status. Unit is second. | SW_HEALTH_CHECKER_INTERVAL_SECONDS | 5 |
 | configuration-discovery | default | disableMessageDigest | If true, agent receives the latest configuration every time even without change. In default, OAP uses SHA512 message digest mechanism to detect changes of configuration. | SW_DISABLE_MESSAGE_DIGEST | false
+| receiver-event|default| Read [receiver doc](backend-receivers.md) for more details | - | - |
 
 ## Notice
 ยน System Environment Variable name could be declared and changed in the application.yml. The names listed here,
diff --git a/oap-server/analyzer/pom.xml b/oap-server/analyzer/event-analyzer/pom.xml
similarity index 66%
copy from oap-server/analyzer/pom.xml
copy to oap-server/analyzer/event-analyzer/pom.xml
index 18dd1c8..6762f47 100644
--- a/oap-server/analyzer/pom.xml
+++ b/oap-server/analyzer/event-analyzer/pom.xml
@@ -19,36 +19,20 @@
 
 <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>
+        <artifactId>analyzer</artifactId>
         <groupId>org.apache.skywalking</groupId>
         <version>8.5.0-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>analyzer</artifactId>
-    <packaging>pom</packaging>
-
-    <modules>
-        <module>agent-analyzer</module>
-        <module>log-analyzer</module>
-        <module>meter-analyzer</module>
-    </modules>
+    <artifactId>event-analyzer</artifactId>
+    <packaging>jar</packaging>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.skywalking</groupId>
-            <artifactId>apm-network</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>library-module</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>library-util</artifactId>
+            <artifactId>server-core</artifactId>
             <version>${project.version}</version>
         </dependency>
     </dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git a/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzer.java b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzer.java
new file mode 100644
index 0000000..77b0394
--- /dev/null
+++ b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzer.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.oap.server.analyzer.event;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.apm.network.event.v3.Event;
+import org.apache.skywalking.oap.server.analyzer.event.listener.EventAnalyzerListener;
+import org.apache.skywalking.oap.server.analyzer.event.listener.EventAnalyzerListenerFactoryManager;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+
+/**
+ * Analyze the collected event data, is the entry point for event analysis.
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class EventAnalyzer {
+    private final ModuleManager moduleManager;
+
+    private final EventAnalyzerListenerFactoryManager factoryManager;
+
+    private final List<EventAnalyzerListener> listeners = new ArrayList<>();
+
+    public void analyze(final Event builder) {
+        createListeners();
+        notifyListener(builder);
+        notifyListenerToBuild();
+    }
+
+    private void notifyListener(final Event event) {
+        listeners.forEach(listener -> listener.parse(event));
+    }
+
+    private void notifyListenerToBuild() {
+        listeners.forEach(EventAnalyzerListener::build);
+    }
+
+    private void createListeners() {
+        factoryManager.factories()
+                      .forEach(factory -> listeners.add(factory.create(moduleManager)));
+    }
+}
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerModule.java
similarity index 65%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerModule.java
index 7475364..df53da7 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerModule.java
@@ -16,16 +16,21 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core;
+package org.apache.skywalking.oap.server.analyzer.event;
 
-import org.junit.Assert;
-import org.junit.Test;
+import org.apache.skywalking.oap.server.library.module.ModuleDefine;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
+public class EventAnalyzerModule extends ModuleDefine {
+    public static final String NAME = "event-analyzer";
 
-        Assert.assertEquals(32, coreModule.services().length);
+    public EventAnalyzerModule() {
+        super(NAME);
+    }
+
+    @Override
+    public Class<?>[] services() {
+        return new Class<?>[] {
+            EventAnalyzerService.class
+        };
     }
 }
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerModuleConfig.java
similarity index 73%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerModuleConfig.java
index 7475364..fe22a72 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerModuleConfig.java
@@ -16,16 +16,9 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core;
+package org.apache.skywalking.oap.server.analyzer.event;
 
-import org.junit.Assert;
-import org.junit.Test;
+import org.apache.skywalking.oap.server.library.module.ModuleConfig;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
-
-        Assert.assertEquals(32, coreModule.services().length);
-    }
+public class EventAnalyzerModuleConfig extends ModuleConfig {
 }
diff --git a/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerModuleProvider.java b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerModuleProvider.java
new file mode 100644
index 0000000..d29e4cb
--- /dev/null
+++ b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerModuleProvider.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.oap.server.analyzer.event;
+
+import org.apache.skywalking.oap.server.analyzer.event.listener.EventRecordAnalyzerListener;
+import org.apache.skywalking.oap.server.core.CoreModule;
+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.ServiceNotProvidedException;
+
+public class EventAnalyzerModuleProvider extends ModuleProvider {
+
+    private EventAnalyzerServiceImpl analysisService;
+
+    @Override
+    public String name() {
+        return "default";
+    }
+
+    @Override
+    public Class<? extends ModuleDefine> module() {
+        return EventAnalyzerModule.class;
+    }
+
+    @Override
+    public ModuleConfig createConfigBeanIfAbsent() {
+        return new EventAnalyzerModuleConfig();
+    }
+
+    @Override
+    public void prepare() throws ServiceNotProvidedException {
+        analysisService = new EventAnalyzerServiceImpl(getManager());
+        registerServiceImplementation(EventAnalyzerService.class, analysisService);
+    }
+
+    @Override
+    public void start() {
+        analysisService.add(new EventRecordAnalyzerListener.Factory(getManager()));
+    }
+
+    @Override
+    public void notifyAfterCompleted() {
+
+    }
+
+    @Override
+    public String[] requiredModules() {
+        return new String[] {
+            CoreModule.NAME
+        };
+    }
+
+}
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerService.java
similarity index 73%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerService.java
index 7475364..4646754 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerService.java
@@ -16,16 +16,11 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core;
+package org.apache.skywalking.oap.server.analyzer.event;
 
-import org.junit.Assert;
-import org.junit.Test;
+import org.apache.skywalking.apm.network.event.v3.Event;
+import org.apache.skywalking.oap.server.library.module.Service;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
-
-        Assert.assertEquals(32, coreModule.services().length);
-    }
+public interface EventAnalyzerService extends Service {
+    void analyze(final Event event);
 }
diff --git a/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerServiceImpl.java b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerServiceImpl.java
new file mode 100644
index 0000000..aa341d3
--- /dev/null
+++ b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/EventAnalyzerServiceImpl.java
@@ -0,0 +1,50 @@
+/*
+ * 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.oap.server.analyzer.event;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.apache.skywalking.apm.network.event.v3.Event;
+import org.apache.skywalking.oap.server.analyzer.event.listener.EventAnalyzerListener;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.analyzer.event.listener.EventAnalyzerListenerFactoryManager;
+
+@RequiredArgsConstructor
+public class EventAnalyzerServiceImpl implements EventAnalyzerService, EventAnalyzerListenerFactoryManager {
+    private final ModuleManager moduleManager;
+
+    private final List<EventAnalyzerListener.Factory> factories = new ArrayList<>();
+
+    @Override
+    public void analyze(final Event event) {
+        final EventAnalyzer analyzer = new EventAnalyzer(moduleManager, this);
+        analyzer.analyze(event);
+    }
+
+    @Override
+    public void add(final EventAnalyzerListener.Factory factory) {
+        factories.add(factory);
+    }
+
+    @Override
+    public List<EventAnalyzerListener.Factory> factories() {
+        return factories;
+    }
+}
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/listener/EventAnalyzerListener.java
similarity index 53%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/listener/EventAnalyzerListener.java
index 7475364..7b4c6b1 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/listener/EventAnalyzerListener.java
@@ -16,16 +16,26 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core;
+package org.apache.skywalking.oap.server.analyzer.event.listener;
 
-import org.junit.Assert;
-import org.junit.Test;
+import org.apache.skywalking.apm.network.event.v3.Event;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
+/**
+ * {@code EventAnalyzerListener} represents the callback when OAP does the event data analysis.
+ */
+public interface EventAnalyzerListener {
+    /**
+     * Parse the raw data from the proto buffer messages.
+     */
+    void parse(Event event);
+
+    /**
+     * The last step of the analysis process. Typically, the implementations forward the analysis results to the source receiver.
+     */
+    void build();
 
-        Assert.assertEquals(32, coreModule.services().length);
+    interface Factory {
+        EventAnalyzerListener create(final ModuleManager moduleManager);
     }
 }
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/listener/EventAnalyzerListenerFactoryManager.java
similarity index 71%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/listener/EventAnalyzerListenerFactoryManager.java
index 7475364..ebb4ba0 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/listener/EventAnalyzerListenerFactoryManager.java
@@ -16,16 +16,14 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core;
+package org.apache.skywalking.oap.server.analyzer.event.listener;
 
-import org.junit.Assert;
-import org.junit.Test;
+import java.util.List;
+import org.apache.skywalking.oap.server.library.module.Service;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
+public interface EventAnalyzerListenerFactoryManager extends Service {
 
-        Assert.assertEquals(32, coreModule.services().length);
-    }
+    void add(EventAnalyzerListener.Factory factory);
+
+    List<EventAnalyzerListener.Factory> factories();
 }
diff --git a/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/listener/EventRecordAnalyzerListener.java b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/listener/EventRecordAnalyzerListener.java
new file mode 100644
index 0000000..cd9e983
--- /dev/null
+++ b/oap-server/analyzer/event-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/event/listener/EventRecordAnalyzerListener.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.analyzer.event.listener;
+
+import com.google.gson.Gson;
+import lombok.RequiredArgsConstructor;
+import org.apache.skywalking.apm.network.event.v3.Source;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
+import org.apache.skywalking.oap.server.core.analysis.worker.MetricsStreamProcessor;
+import org.apache.skywalking.oap.server.core.config.NamingControl;
+import org.apache.skywalking.oap.server.core.event.Event;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+
+/**
+ * EventRecordAnalyzerListener forwards the event data to the persistence layer with the query required conditions.
+ */
+@RequiredArgsConstructor
+public class EventRecordAnalyzerListener implements EventAnalyzerListener {
+    private static final Gson GSON = new Gson();
+
+    private final NamingControl namingControl;
+
+    private final Event event = new Event();
+
+    @Override
+    public void build() {
+        MetricsStreamProcessor.getInstance().in(event);
+    }
+
+    @Override
+    public void parse(final org.apache.skywalking.apm.network.event.v3.Event e) {
+        event.setUuid(e.getUuid());
+
+        if (e.hasSource()) {
+            final Source source = e.getSource();
+            event.setService(namingControl.formatServiceName(source.getService()));
+            event.setServiceInstance(namingControl.formatInstanceName(source.getServiceInstance()));
+            event.setEndpoint(namingControl.formatEndpointName(source.getService(), source.getEndpoint()));
+        }
+
+        event.setName(e.getName());
+        event.setType(e.getType().name());
+        event.setMessage(e.getMessage());
+        event.setParameters(GSON.toJson(e.getParametersMap()));
+        event.setStartTime(e.getStartTime());
+        event.setEndTime(e.getEndTime());
+        event.setTimeBucket(TimeBucket.getMinuteTimeBucket(e.getStartTime()));
+    }
+
+    public static class Factory implements EventAnalyzerListener.Factory {
+        private final NamingControl namingControl;
+
+        public Factory(final ModuleManager moduleManager) {
+            this.namingControl = moduleManager.find(CoreModule.NAME)
+                                              .provider()
+                                              .getService(NamingControl.class);
+        }
+
+        @Override
+        public EventAnalyzerListener create(final ModuleManager moduleManager) {
+            return new EventRecordAnalyzerListener(namingControl);
+        }
+    }
+}
diff --git a/oap-server/analyzer/event-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine b/oap-server/analyzer/event-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
new file mode 100644
index 0000000..1255490
--- /dev/null
+++ b/oap-server/analyzer/event-analyzer/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.oap.server.analyzer.event.EventAnalyzerModule
diff --git a/oap-server/analyzer/event-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider b/oap-server/analyzer/event-analyzer/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
new file mode 100644
index 0000000..15c0414
--- /dev/null
+++ b/oap-server/analyzer/event-analyzer/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.oap.server.analyzer.event.EventAnalyzerModuleProvider
diff --git a/oap-server/analyzer/pom.xml b/oap-server/analyzer/pom.xml
index 18dd1c8..92893ff 100644
--- a/oap-server/analyzer/pom.xml
+++ b/oap-server/analyzer/pom.xml
@@ -32,6 +32,7 @@
         <module>agent-analyzer</module>
         <module>log-analyzer</module>
         <module>meter-analyzer</module>
+        <module>event-analyzer</module>
     </modules>
 
     <dependencies>
@@ -51,4 +52,4 @@
             <version>${project.version}</version>
         </dependency>
     </dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git a/oap-server/server-bootstrap/pom.xml b/oap-server/server-bootstrap/pom.xml
index 8a4f183..46d9062 100644
--- a/oap-server/server-bootstrap/pom.xml
+++ b/oap-server/server-bootstrap/pom.xml
@@ -136,6 +136,11 @@
             <artifactId>configuration-discovery-receiver-plugin</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>skywalking-event-receiver-plugin</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <!-- receiver module -->
 
         <!-- fetcher module -->
diff --git a/oap-server/server-bootstrap/src/main/resources/application.yml b/oap-server/server-bootstrap/src/main/resources/application.yml
index 557e925..5b9c1c4 100755
--- a/oap-server/server-bootstrap/src/main/resources/application.yml
+++ b/oap-server/server-bootstrap/src/main/resources/application.yml
@@ -219,6 +219,10 @@ log-analyzer:
   selector: ${SW_LOG_ANALYZER:default}
   default:
 
+event-analyzer:
+  selector: ${SW_EVENT_ANALYZER:default}
+  default:
+
 receiver-sharing-server:
   selector: ${SW_RECEIVER_SHARING_SERVER:default}
   default:
@@ -422,3 +426,7 @@ configuration-discovery:
   selector: ${SW_CONFIGURATION_DISCOVERY:default}
   default:
     disableMessageDigest: ${SW_DISABLE_MESSAGE_DIGEST:false}
+
+receiver-event:
+  selector: ${SW_RECEIVER_EVENT:default}
+  default:
diff --git a/oap-server/server-bootstrap/src/main/resources/log4j2.xml b/oap-server/server-bootstrap/src/main/resources/log4j2.xml
index dde0c31..a14272f 100644
--- a/oap-server/server-bootstrap/src/main/resources/log4j2.xml
+++ b/oap-server/server-bootstrap/src/main/resources/log4j2.xml
@@ -33,6 +33,7 @@
         <logger name="org.apache.http" level="INFO"/>
         <logger name="org.apache.skywalking.oap.server.core.alarm.AlarmStandardPersistence" level="DEBUG"/>
         <logger name="org.apache.skywalking.oap.server.core" level="DEBUG"/>
+        <logger name="org.apache.skywalking.oap.server.core.storage.PersistenceTimer" level="INFO"/>
         <logger name="org.apache.skywalking.oap.server.core.analysis.worker" level="DEBUG" />
         <logger name="org.apache.skywalking.oap.server.core.remote.client" level="DEBUG"/>
         <logger name="org.apache.skywalking.oap.server.library.buffer" level="INFO"/>
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModule.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModule.java
index 358bc28..9793a7e 100755
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModule.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModule.java
@@ -34,6 +34,7 @@ import org.apache.skywalking.oap.server.core.profile.ProfileTaskMutationService;
 import org.apache.skywalking.oap.server.core.query.AggregationQueryService;
 import org.apache.skywalking.oap.server.core.query.AlarmQueryService;
 import org.apache.skywalking.oap.server.core.query.BrowserLogQueryService;
+import org.apache.skywalking.oap.server.core.query.EventQueryService;
 import org.apache.skywalking.oap.server.core.query.LogQueryService;
 import org.apache.skywalking.oap.server.core.query.MetadataQueryService;
 import org.apache.skywalking.oap.server.core.query.MetricsMetadataQueryService;
@@ -116,6 +117,7 @@ public class CoreModule extends ModuleDefine {
         classes.add(AlarmQueryService.class);
         classes.add(TopNRecordsQueryService.class);
         classes.add(BrowserLogQueryService.class);
+        classes.add(EventQueryService.class);
     }
 
     private void addServerInterface(List<Class> classes) {
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleProvider.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleProvider.java
index dbaba79..cbdd38d 100755
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleProvider.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/CoreModuleProvider.java
@@ -54,6 +54,7 @@ import org.apache.skywalking.oap.server.core.profile.ProfileTaskMutationService;
 import org.apache.skywalking.oap.server.core.query.AggregationQueryService;
 import org.apache.skywalking.oap.server.core.query.AlarmQueryService;
 import org.apache.skywalking.oap.server.core.query.BrowserLogQueryService;
+import org.apache.skywalking.oap.server.core.query.EventQueryService;
 import org.apache.skywalking.oap.server.core.query.LogQueryService;
 import org.apache.skywalking.oap.server.core.query.MetadataQueryService;
 import org.apache.skywalking.oap.server.core.query.MetricsMetadataQueryService;
@@ -250,6 +251,7 @@ public class CoreModuleProvider extends ModuleProvider {
         this.registerServiceImplementation(AggregationQueryService.class, new AggregationQueryService(getManager()));
         this.registerServiceImplementation(AlarmQueryService.class, new AlarmQueryService(getManager()));
         this.registerServiceImplementation(TopNRecordsQueryService.class, new TopNRecordsQueryService(getManager()));
+        this.registerServiceImplementation(EventQueryService.class, new EventQueryService(getManager()));
 
         // add profile service implementations
         this.registerServiceImplementation(
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/event/Event.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/event/Event.java
new file mode 100644
index 0000000..fd87d55
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/event/Event.java
@@ -0,0 +1,212 @@
+/*
+ * 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.oap.server.core.event;
+
+import java.util.HashMap;
+import java.util.Map;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.skywalking.apm.util.StringUtil;
+import org.apache.skywalking.oap.server.core.analysis.MetricsExtension;
+import org.apache.skywalking.oap.server.core.analysis.Stream;
+import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics;
+import org.apache.skywalking.oap.server.core.analysis.worker.MetricsStreamProcessor;
+import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData;
+import org.apache.skywalking.oap.server.core.source.ScopeDeclaration;
+import org.apache.skywalking.oap.server.core.storage.StorageBuilder;
+import org.apache.skywalking.oap.server.core.storage.annotation.Column;
+
+import static org.apache.skywalking.oap.server.core.source.DefaultScopeDefine.EVENT;
+
+@Getter
+@Setter
+@ScopeDeclaration(id = EVENT, name = "Event")
+@Stream(name = Event.INDEX_NAME, scopeId = EVENT, builder = Event.Builder.class, processor = MetricsStreamProcessor.class)
+@EqualsAndHashCode(
+    callSuper = false,
+    of = "uuid"
+)
+@MetricsExtension(supportDownSampling = false, supportUpdate = true)
+public class Event extends Metrics {
+
+    public static final String INDEX_NAME = "events";
+
+    public static final String UUID = "uuid";
+
+    public static final String SERVICE = "service";
+
+    public static final String SERVICE_INSTANCE = "service_instance";
+
+    public static final String ENDPOINT = "endpoint";
+
+    public static final String NAME = "name";
+
+    public static final String TYPE = "type";
+
+    public static final String MESSAGE = "message";
+
+    public static final String PARAMETERS = "parameters";
+
+    public static final String START_TIME = "start_time";
+
+    public static final String END_TIME = "end_time";
+
+    @Override
+    public String id() {
+        return getUuid();
+    }
+
+    @Column(columnName = UUID)
+    private String uuid;
+
+    @Column(columnName = SERVICE)
+    private String service;
+
+    @Column(columnName = SERVICE_INSTANCE)
+    private String serviceInstance;
+
+    @Column(columnName = ENDPOINT)
+    private String endpoint;
+
+    @Column(columnName = NAME)
+    private String name;
+
+    @Column(columnName = TYPE)
+    private String type;
+
+    @Column(columnName = MESSAGE)
+    private String message;
+
+    @Column(columnName = PARAMETERS, storageOnly = true)
+    private String parameters;
+
+    @Column(columnName = START_TIME)
+    private long startTime;
+
+    @Column(columnName = END_TIME)
+    private long endTime;
+
+    @Override
+    public void combine(final Metrics metrics) {
+        final Event event = (Event) metrics;
+        setEndTime(event.getEndTime());
+
+        if (StringUtil.isNotBlank(event.getType())) {
+            setType(event.getType());
+        }
+        if (StringUtil.isNotBlank(event.getMessage())) {
+            setType(event.getMessage());
+        }
+        if (StringUtil.isNotBlank(event.getParameters())) {
+            setParameters(event.getParameters());
+        }
+    }
+
+    @Override
+    public void calculate() {
+    }
+
+    @Override
+    public Metrics toHour() {
+        return null;
+    }
+
+    @Override
+    public Metrics toDay() {
+        return null;
+    }
+
+    @Override
+    public void deserialize(final RemoteData remoteData) {
+        setUuid(remoteData.getDataStrings(0));
+        setService(remoteData.getDataStrings(1));
+        setServiceInstance(remoteData.getDataStrings(2));
+        setEndpoint(remoteData.getDataStrings(3));
+        setName(remoteData.getDataStrings(4));
+        setType(remoteData.getDataStrings(5));
+        setMessage(remoteData.getDataStrings(6));
+        setParameters(remoteData.getDataStrings(7));
+
+        setStartTime(remoteData.getDataLongs(0));
+        setEndTime(remoteData.getDataLongs(1));
+        setTimeBucket(remoteData.getDataLongs(2));
+    }
+
+    @Override
+    public RemoteData.Builder serialize() {
+        final RemoteData.Builder builder = RemoteData.newBuilder();
+
+        builder.addDataStrings(getUuid());
+        builder.addDataStrings(getService());
+        builder.addDataStrings(getServiceInstance());
+        builder.addDataStrings(getEndpoint());
+        builder.addDataStrings(getName());
+        builder.addDataStrings(getType());
+        builder.addDataStrings(getMessage());
+        builder.addDataStrings(getParameters());
+
+        builder.addDataLongs(getStartTime());
+        builder.addDataLongs(getEndTime());
+        builder.addDataLongs(getTimeBucket());
+
+        return builder;
+    }
+
+    @Override
+    public int remoteHashCode() {
+        return hashCode();
+    }
+
+    public static class Builder implements StorageBuilder<Event> {
+        @Override
+        public Map<String, Object> data2Map(Event storageData) {
+            Map<String, Object> map = new HashMap<>();
+            map.put(UUID, storageData.getUuid());
+            map.put(SERVICE, storageData.getService());
+            map.put(SERVICE_INSTANCE, storageData.getServiceInstance());
+            map.put(ENDPOINT, storageData.getEndpoint());
+            map.put(NAME, storageData.getName());
+            map.put(TYPE, storageData.getType());
+            map.put(MESSAGE, storageData.getMessage());
+            map.put(PARAMETERS, storageData.getParameters());
+            map.put(START_TIME, storageData.getStartTime());
+            map.put(END_TIME, storageData.getEndTime());
+            map.put(TIME_BUCKET, storageData.getTimeBucket());
+            return map;
+        }
+
+        @Override
+        public Event map2Data(Map<String, Object> dbMap) {
+            Event record = new Event();
+            record.setUuid((String) dbMap.get(UUID));
+            record.setService((String) dbMap.get(SERVICE));
+            record.setServiceInstance((String) dbMap.get(SERVICE_INSTANCE));
+            record.setEndpoint((String) dbMap.get(ENDPOINT));
+            record.setName((String) dbMap.get(NAME));
+            record.setType((String) dbMap.get(TYPE));
+            record.setMessage((String) dbMap.get(MESSAGE));
+            record.setParameters((String) dbMap.get(PARAMETERS));
+            record.setStartTime(((Number) dbMap.get(START_TIME)).longValue());
+            record.setEndTime(((Number) dbMap.get(END_TIME)).longValue());
+            record.setTimeBucket(((Number) dbMap.get(TIME_BUCKET)).longValue());
+            return record;
+        }
+    }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/EventQueryService.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/EventQueryService.java
new file mode 100644
index 0000000..a489fde
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/EventQueryService.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.core.query;
+
+import org.apache.skywalking.oap.server.core.query.input.Duration;
+import org.apache.skywalking.oap.server.core.query.type.event.EventQueryCondition;
+import org.apache.skywalking.oap.server.core.query.type.event.Events;
+import org.apache.skywalking.oap.server.core.storage.StorageModule;
+import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.module.Service;
+
+import static java.util.Objects.isNull;
+import static org.apache.skywalking.apm.util.StringUtil.isBlank;
+
+public class EventQueryService implements Service {
+
+    private final ModuleManager moduleManager;
+
+    private IEventQueryDAO dao;
+
+    public EventQueryService(ModuleManager moduleManager) {
+        this.moduleManager = moduleManager;
+    }
+
+    private IEventQueryDAO getDao() {
+        if (dao == null) {
+            dao = moduleManager.find(StorageModule.NAME).provider().getService(IEventQueryDAO.class);
+        }
+        return dao;
+    }
+
+    public Events queryEvents(final EventQueryCondition condition) throws Exception {
+        if (isBlank(condition.getUuid()) && isDurationInvalid(condition.getTime())) {
+            throw new IllegalArgumentException("time field is required when uuid is absent.");
+        }
+        return getDao().queryEvents(condition);
+    }
+
+    boolean isDurationInvalid(final Duration duration) {
+        return isNull(duration) || (isBlank(duration.getStart()) || isBlank(duration.getEnd()));
+    }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/Event.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/Event.java
new file mode 100644
index 0000000..7b2d17e
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/Event.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.core.query.type.event;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import lombok.Data;
+import org.apache.skywalking.apm.util.StringUtil;
+import org.apache.skywalking.oap.server.core.query.type.KeyValue;
+
+@Data
+public class Event {
+    private static final Gson GSON = new Gson();
+
+    private String uuid;
+
+    private Source source;
+
+    private String name;
+
+    private EventType type;
+
+    private String message;
+
+    private List<KeyValue> parameters;
+
+    private long startTime;
+
+    private long endTime;
+
+    public void setParameters(final List<KeyValue> parameters) {
+        this.parameters = parameters;
+    }
+
+    public void setParameters(final String json) {
+        if (StringUtil.isNotEmpty(json)) {
+            final Map<String, String> map = GSON.fromJson(json, new TypeToken<Map<String, String>>() {
+            }.getType());
+            this.parameters = map.entrySet().stream().map(e -> new KeyValue(e.getKey(), e.getValue())).collect(Collectors.toList());
+        }
+    }
+
+}
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/EventQueryCondition.java
similarity index 52%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/EventQueryCondition.java
index 7475364..30009a8 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/EventQueryCondition.java
@@ -16,16 +16,32 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core;
+package org.apache.skywalking.oap.server.core.query.type.event;
 
-import org.junit.Assert;
-import org.junit.Test;
+import lombok.Data;
+import org.apache.skywalking.oap.server.core.query.enumeration.Order;
+import org.apache.skywalking.oap.server.core.query.input.Duration;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
+import static org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO.DEFAULT_SIZE;
+import static org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO.MAX_SIZE;
 
-        Assert.assertEquals(32, coreModule.services().length);
+@Data
+public class EventQueryCondition {
+    private String uuid;
+
+    private Source source;
+
+    private String name;
+
+    private EventType type;
+
+    private Duration time;
+
+    private Order order;
+
+    private int size;
+
+    public int getSize() {
+        return size > 0 ? Math.min(size, MAX_SIZE) : DEFAULT_SIZE;
     }
 }
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/EventType.java
similarity index 63%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/EventType.java
index 7475364..9b534af 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/EventType.java
@@ -16,16 +16,20 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core;
+package org.apache.skywalking.oap.server.core.query.type.event;
 
-import org.junit.Assert;
-import org.junit.Test;
+import com.google.common.base.Strings;
+import java.util.Objects;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
+public enum EventType {
+    Normal, Error;
 
-        Assert.assertEquals(32, coreModule.services().length);
+    public static EventType parse(final String raw) {
+        for (final EventType value : EventType.values()) {
+            if (Objects.equals(value.name().toLowerCase(), Strings.nullToEmpty(raw).toLowerCase())) {
+                return value;
+            }
+        }
+        return Normal;
     }
 }
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/Events.java
similarity index 73%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/Events.java
index 7475364..fe9afa2 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/Events.java
@@ -16,16 +16,15 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core;
+package org.apache.skywalking.oap.server.core.query.type.event;
 
-import org.junit.Assert;
-import org.junit.Test;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Data;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
+@Data
+public class Events {
+    private List<Event> events = new ArrayList<>();
 
-        Assert.assertEquals(32, coreModule.services().length);
-    }
+    private long total;
 }
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/Source.java
similarity index 72%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/Source.java
index 7475364..98dd2b7 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/type/event/Source.java
@@ -16,16 +16,17 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core;
+package org.apache.skywalking.oap.server.core.query.type.event;
 
-import org.junit.Assert;
-import org.junit.Test;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
-
-        Assert.assertEquals(32, coreModule.services().length);
-    }
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class Source {
+    private String service;
+    private String serviceInstance;
+    private String endpoint;
 }
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/DefaultScopeDefine.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/DefaultScopeDefine.java
index ee09a36..f87d82b 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/DefaultScopeDefine.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/DefaultScopeDefine.java
@@ -81,6 +81,8 @@ public class DefaultScopeDefine {
     public static final int LOG = 41;
     public static final int ENDPOINT_META = 42;
 
+    public static final int EVENT = 43;
+
     /**
      * Catalog of scope, the metrics processor could use this to group all generated metrics by oal rt.
      */
@@ -338,4 +340,4 @@ public class DefaultScopeDefine {
         }
         return scopeDefaultColumns;
     }
-}
\ No newline at end of file
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/StorageModule.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/StorageModule.java
index 4f72c0c..842a27a 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/StorageModule.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/StorageModule.java
@@ -26,6 +26,7 @@ import org.apache.skywalking.oap.server.core.storage.profile.IProfileThreadSnaps
 import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IAlarmQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IBrowserLogQueryDAO;
+import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.ILogQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetadataQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
@@ -65,7 +66,8 @@ public class StorageModule extends ModuleDefine {
                 IProfileTaskLogQueryDAO.class,
                 IProfileThreadSnapshotQueryDAO.class,
                 UITemplateManagementDAO.class,
-                IBrowserLogQueryDAO.class
+                IBrowserLogQueryDAO.class,
+                IEventQueryDAO.class
         };
     }
 }
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/query/IEventQueryDAO.java
similarity index 64%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/query/IEventQueryDAO.java
index 7475364..e377226 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/storage/query/IEventQueryDAO.java
@@ -16,16 +16,15 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core;
+package org.apache.skywalking.oap.server.core.storage.query;
 
-import org.junit.Assert;
-import org.junit.Test;
+import org.apache.skywalking.oap.server.core.query.type.event.EventQueryCondition;
+import org.apache.skywalking.oap.server.core.query.type.event.Events;
+import org.apache.skywalking.oap.server.core.storage.DAO;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
+public interface IEventQueryDAO extends DAO {
+    int DEFAULT_SIZE = 20;
+    int MAX_SIZE = 100;
 
-        Assert.assertEquals(32, coreModule.services().length);
-    }
+    Events queryEvents(final EventQueryCondition condition) throws Exception;
 }
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
index 7475364..ef116df 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
@@ -26,6 +26,6 @@ public class CoreModuleTest {
     public void testOpenServiceList() {
         CoreModule coreModule = new CoreModule();
 
-        Assert.assertEquals(32, coreModule.services().length);
+        Assert.assertEquals(33, coreModule.services().length);
     }
 }
diff --git a/oap-server/server-library/library-server/pom.xml b/oap-server/server-library/library-server/pom.xml
index e2a9d3a..7eb02a5 100644
--- a/oap-server/server-library/library-server/pom.xml
+++ b/oap-server/server-library/library-server/pom.xml
@@ -60,4 +60,4 @@
             <artifactId>jetty-servlet</artifactId>
         </dependency>
     </dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryProvider.java b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryProvider.java
index 82c4d8c..f912198 100644
--- a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryProvider.java
+++ b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryProvider.java
@@ -24,6 +24,7 @@ import graphql.schema.GraphQLSchema;
 import org.apache.skywalking.oap.query.graphql.resolver.AggregationQuery;
 import org.apache.skywalking.oap.query.graphql.resolver.AlarmQuery;
 import org.apache.skywalking.oap.query.graphql.resolver.BrowserLogQuery;
+import org.apache.skywalking.oap.query.graphql.resolver.EventQuery;
 import org.apache.skywalking.oap.query.graphql.resolver.HealthQuery;
 import org.apache.skywalking.oap.query.graphql.resolver.LogQuery;
 import org.apache.skywalking.oap.query.graphql.resolver.MetadataQuery;
@@ -107,6 +108,8 @@ public class GraphQLQueryProvider extends ModuleProvider {
                                            .resolvers(new UIConfigurationManagement(getManager()))
                                            .file("query-protocol/browser-log.graphqls")
                                            .resolvers(new BrowserLogQuery(getManager()))
+                                           .file("query-protocol/event.graphqls")
+                                           .resolvers(new EventQuery(getManager()))
                                            .build()
                                            .makeExecutableSchema();
         this.graphQL = GraphQL.newGraphQL(schema).build();
diff --git a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/EventQuery.java b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/EventQuery.java
new file mode 100644
index 0000000..aa9f8b7
--- /dev/null
+++ b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/EventQuery.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.oap.query.graphql.resolver;
+
+import com.coxautodev.graphql.tools.GraphQLQueryResolver;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.query.EventQueryService;
+import org.apache.skywalking.oap.server.core.query.type.event.EventQueryCondition;
+import org.apache.skywalking.oap.server.core.query.type.event.Events;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+
+public class EventQuery implements GraphQLQueryResolver {
+    private EventQueryService queryService;
+
+    private final ModuleManager moduleManager;
+
+    public EventQuery(ModuleManager moduleManager) {
+        this.moduleManager = moduleManager;
+    }
+
+    EventQueryService queryService() {
+        if (queryService != null) {
+            return queryService;
+        }
+
+        queryService = moduleManager.find(CoreModule.NAME)
+                                    .provider()
+                                    .getService(EventQueryService.class);
+        return queryService;
+    }
+
+    public Events queryEvents(final EventQueryCondition condition) throws Exception {
+        return queryService().queryEvents(condition);
+    }
+}
diff --git a/oap-server/server-receiver-plugin/pom.xml b/oap-server/server-receiver-plugin/pom.xml
index e744018..a08c77a 100644
--- a/oap-server/server-receiver-plugin/pom.xml
+++ b/oap-server/server-receiver-plugin/pom.xml
@@ -44,6 +44,7 @@
         <module>skywalking-browser-receiver-plugin</module>
         <module>skywalking-log-recevier-plugin</module>
         <module>configuration-discovery-receiver-plugin</module>
+        <module>skywalking-event-receiver-plugin</module>
     </modules>
 
     <dependencies>
@@ -68,4 +69,4 @@
             <version>${project.version}</version>
         </dependency>
     </dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git a/oap-server/analyzer/pom.xml b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/pom.xml
similarity index 73%
copy from oap-server/analyzer/pom.xml
copy to oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/pom.xml
index 18dd1c8..f534691 100644
--- a/oap-server/analyzer/pom.xml
+++ b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/pom.xml
@@ -19,36 +19,25 @@
 
 <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>
+        <artifactId>server-receiver-plugin</artifactId>
         <groupId>org.apache.skywalking</groupId>
         <version>8.5.0-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>analyzer</artifactId>
-    <packaging>pom</packaging>
-
-    <modules>
-        <module>agent-analyzer</module>
-        <module>log-analyzer</module>
-        <module>meter-analyzer</module>
-    </modules>
+    <artifactId>skywalking-event-receiver-plugin</artifactId>
+    <packaging>jar</packaging>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.skywalking</groupId>
-            <artifactId>apm-network</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>library-module</artifactId>
+            <artifactId>event-analyzer</artifactId>
             <version>${project.version}</version>
         </dependency>
         <dependency>
             <groupId>org.apache.skywalking</groupId>
-            <artifactId>library-util</artifactId>
+            <artifactId>skywalking-sharing-server-plugin</artifactId>
             <version>${project.version}</version>
         </dependency>
     </dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/event/EventModule.java
similarity index 69%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/event/EventModule.java
index 7475364..f1284ef 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/event/EventModule.java
@@ -16,16 +16,19 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core;
+package org.apache.skywalking.oap.server.receiver.event;
 
-import org.junit.Assert;
-import org.junit.Test;
+import org.apache.skywalking.oap.server.library.module.ModuleDefine;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
+public class EventModule extends ModuleDefine {
+    public static final String NAME = "receiver-event";
 
-        Assert.assertEquals(32, coreModule.services().length);
+    public EventModule() {
+        super(NAME);
+    }
+
+    @Override
+    public Class<?>[] services() {
+        return new Class<?>[0];
     }
 }
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/event/EventModuleConfig.java
similarity index 73%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/event/EventModuleConfig.java
index 7475364..58ce63d 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/event/EventModuleConfig.java
@@ -16,16 +16,9 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core;
+package org.apache.skywalking.oap.server.receiver.event;
 
-import org.junit.Assert;
-import org.junit.Test;
+import org.apache.skywalking.oap.server.library.module.ModuleConfig;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
-
-        Assert.assertEquals(32, coreModule.services().length);
-    }
+public class EventModuleConfig extends ModuleConfig {
 }
diff --git a/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/event/EventModuleProvider.java b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/event/EventModuleProvider.java
new file mode 100755
index 0000000..5ba2a2f
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/event/EventModuleProvider.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.receiver.event;
+
+import org.apache.skywalking.oap.server.analyzer.event.EventAnalyzerModule;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegister;
+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.ServiceNotProvidedException;
+import org.apache.skywalking.oap.server.receiver.event.grpc.EventGrpcServiceHandler;
+import org.apache.skywalking.oap.server.receiver.sharing.server.SharingServerModule;
+
+public class EventModuleProvider extends ModuleProvider {
+
+    @Override
+    public String name() {
+        return "default";
+    }
+
+    @Override
+    public Class<? extends ModuleDefine> module() {
+        return EventModule.class;
+    }
+
+    @Override
+    public ModuleConfig createConfigBeanIfAbsent() {
+        return new EventModuleConfig();
+    }
+
+    @Override
+    public void prepare() throws ServiceNotProvidedException {
+    }
+
+    @Override
+    public void start() {
+        final GRPCHandlerRegister grpcHandlerRegister = getManager().find(SharingServerModule.NAME)
+                                                                    .provider()
+                                                                    .getService(GRPCHandlerRegister.class);
+        final EventGrpcServiceHandler eventGRPCServiceHandler = new EventGrpcServiceHandler(getManager());
+        grpcHandlerRegister.addHandler(eventGRPCServiceHandler);
+    }
+
+    @Override
+    public void notifyAfterCompleted() {
+
+    }
+
+    @Override
+    public String[] requiredModules() {
+        return new String[] {
+            CoreModule.NAME,
+            EventAnalyzerModule.NAME,
+            SharingServerModule.NAME
+        };
+    }
+
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/event/grpc/EventGrpcServiceHandler.java b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/event/grpc/EventGrpcServiceHandler.java
new file mode 100644
index 0000000..f7269fa
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/event/grpc/EventGrpcServiceHandler.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.oap.server.receiver.event.grpc;
+
+import io.grpc.stub.StreamObserver;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.apm.network.common.v3.Commands;
+import org.apache.skywalking.apm.network.event.v3.Event;
+import org.apache.skywalking.apm.network.event.v3.EventServiceGrpc;
+import org.apache.skywalking.oap.server.analyzer.event.EventAnalyzerModule;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.server.grpc.GRPCHandler;
+import org.apache.skywalking.oap.server.analyzer.event.EventAnalyzerService;
+import org.apache.skywalking.oap.server.receiver.event.EventModule;
+import org.apache.skywalking.oap.server.telemetry.TelemetryModule;
+import org.apache.skywalking.oap.server.telemetry.api.CounterMetrics;
+import org.apache.skywalking.oap.server.telemetry.api.HistogramMetrics;
+import org.apache.skywalking.oap.server.telemetry.api.MetricsCreator;
+import org.apache.skywalking.oap.server.telemetry.api.MetricsTag;
+
+@Slf4j
+public class EventGrpcServiceHandler extends EventServiceGrpc.EventServiceImplBase implements GRPCHandler {
+    private final HistogramMetrics histogram;
+
+    private final CounterMetrics errorCounter;
+
+    private final EventAnalyzerService eventAnalyzerService;
+
+    public EventGrpcServiceHandler(ModuleManager moduleManager) {
+        final MetricsCreator metricsCreator = moduleManager.find(TelemetryModule.NAME)
+                                                           .provider()
+                                                           .getService(MetricsCreator.class);
+
+        eventAnalyzerService = moduleManager.find(EventAnalyzerModule.NAME)
+                                            .provider()
+                                            .getService(EventAnalyzerService.class);
+
+        histogram = metricsCreator.createHistogramMetric(
+            "event_in_latency", "The process latency of event data",
+            new MetricsTag.Keys("protocol"), new MetricsTag.Values("grpc")
+        );
+        errorCounter = metricsCreator.createCounter(
+            "event_error_count", "The error number of event analysis",
+            new MetricsTag.Keys("protocol"), new MetricsTag.Values("grpc")
+        );
+    }
+
+    @Override
+    public StreamObserver<Event> collect(StreamObserver<Commands> responseObserver) {
+        return new StreamObserver<Event>() {
+            @Override
+            public void onNext(final Event event) {
+                try (HistogramMetrics.Timer ignored = histogram.createTimer()) {
+                    eventAnalyzerService.analyze(event);
+                } catch (Exception e) {
+                    errorCounter.inc();
+                    log.error(e.getMessage(), e);
+                }
+            }
+
+            @Override
+            public void onError(Throwable throwable) {
+                log.error(throwable.getMessage(), throwable);
+                responseObserver.onCompleted();
+            }
+
+            @Override
+            public void onCompleted() {
+                responseObserver.onNext(Commands.newBuilder().build());
+                responseObserver.onCompleted();
+            }
+        };
+    }
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
new file mode 100644
index 0000000..8d79a90
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/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.oap.server.receiver.event.EventModule
diff --git a/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/src/main/resources/META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleProvider
new file mode 100644
index 0000000..827920c
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-event-receiver-plugin/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.oap.server.receiver.event.EventModuleProvider
diff --git a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/StorageModuleElasticsearchProvider.java b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/StorageModuleElasticsearchProvider.java
index 5867bae..b772e57 100644
--- a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/StorageModuleElasticsearchProvider.java
+++ b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/StorageModuleElasticsearchProvider.java
@@ -43,6 +43,7 @@ import org.apache.skywalking.oap.server.core.storage.profile.IProfileThreadSnaps
 import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IAlarmQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IBrowserLogQueryDAO;
+import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.ILogQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetadataQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
@@ -66,6 +67,7 @@ import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.cache.Netwo
 import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.AggregationQueryEsDAO;
 import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.AlarmQueryEsDAO;
 import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.BrowserLogQueryEsDAO;
+import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.ESEventQueryDAO;
 import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.LogQueryEsDAO;
 import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.MetadataQueryEsDAO;
 import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.MetricsQueryEsDAO;
@@ -188,6 +190,8 @@ public class StorageModuleElasticsearchProvider extends ModuleProvider {
                 .getProfileTaskQueryMaxSize()));
         this.registerServiceImplementation(
             UITemplateManagementDAO.class, new UITemplateManagementEsDAO(elasticSearchClient));
+
+        this.registerServiceImplementation(IEventQueryDAO.class, new ESEventQueryDAO(elasticSearchClient));
     }
 
     @Override
diff --git a/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/ESEventQueryDAO.java b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/ESEventQueryDAO.java
new file mode 100644
index 0000000..a2bb62c
--- /dev/null
+++ b/oap-server/server-storage-plugin/storage-elasticsearch-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch/query/ESEventQueryDAO.java
@@ -0,0 +1,140 @@
+/*
+ * 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.oap.server.storage.plugin.elasticsearch.query;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.skywalking.oap.server.core.event.Event;
+import org.apache.skywalking.oap.server.core.query.enumeration.Order;
+import org.apache.skywalking.oap.server.core.query.input.Duration;
+import org.apache.skywalking.oap.server.core.query.type.event.EventQueryCondition;
+import org.apache.skywalking.oap.server.core.query.type.event.EventType;
+import org.apache.skywalking.oap.server.core.query.type.event.Events;
+import org.apache.skywalking.oap.server.core.query.type.event.Source;
+import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
+import org.apache.skywalking.oap.server.library.client.elasticsearch.ElasticSearchClient;
+import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.base.EsDAO;
+import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.base.MatchCNameBuilder;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.index.query.BoolQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.search.SearchHit;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
+import org.elasticsearch.search.sort.SortOrder;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static java.util.Objects.isNull;
+
+public class ESEventQueryDAO extends EsDAO implements IEventQueryDAO {
+    public ESEventQueryDAO(ElasticSearchClient client) {
+        super(client);
+    }
+
+    @Override
+    public Events queryEvents(final EventQueryCondition condition) throws Exception {
+        final SearchSourceBuilder sourceBuilder = buildQuery(condition);
+
+        final SearchResponse response = getClient().search(Event.INDEX_NAME, sourceBuilder);
+
+        final Events events = new Events();
+        events.setTotal((int) response.getHits().totalHits);
+        events.setEvents(Stream.of(response.getHits().getHits())
+                               .map(this::parseSearchHit)
+                               .collect(Collectors.toList()));
+
+        return events;
+    }
+
+    protected SearchSourceBuilder buildQuery(final EventQueryCondition condition) {
+        final SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource();
+        final BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
+        sourceBuilder.query(boolQueryBuilder);
+
+        final List<QueryBuilder> mustQueryList = boolQueryBuilder.must();
+
+        if (!isNullOrEmpty(condition.getUuid())) {
+            mustQueryList.add(QueryBuilders.termQuery(Event.UUID, condition.getUuid()));
+        }
+
+        final Source source = condition.getSource();
+        if (source != null) {
+            if (!isNullOrEmpty(source.getService())) {
+                mustQueryList.add(QueryBuilders.termQuery(Event.SERVICE, source.getService()));
+            }
+            if (!isNullOrEmpty(source.getServiceInstance())) {
+                mustQueryList.add(QueryBuilders.termQuery(Event.SERVICE_INSTANCE, source.getServiceInstance()));
+            }
+            if (!isNullOrEmpty(source.getEndpoint())) {
+                mustQueryList.add(QueryBuilders.matchPhraseQuery(MatchCNameBuilder.INSTANCE.build(Event.ENDPOINT), source.getEndpoint()));
+            }
+        }
+
+        if (!isNullOrEmpty(condition.getName())) {
+            mustQueryList.add(QueryBuilders.termQuery(Event.NAME, condition.getName()));
+        }
+
+        if (condition.getType() != null) {
+            mustQueryList.add(QueryBuilders.termQuery(Event.TYPE, condition.getType().name()));
+        }
+
+        final Duration startTime = condition.getTime();
+        if (startTime != null) {
+            if (startTime.getStartTimestamp() > 0) {
+                mustQueryList.add(QueryBuilders.rangeQuery(Event.START_TIME)
+                                               .gt(startTime.getStartTimestamp()));
+            }
+            if (startTime.getEndTimestamp() > 0) {
+                mustQueryList.add(QueryBuilders.rangeQuery(Event.END_TIME)
+                                               .lt(startTime.getEndTimestamp()));
+            }
+        }
+
+        final Order queryOrder = isNull(condition.getOrder()) ? Order.DES : condition.getOrder();
+        sourceBuilder.sort(Event.START_TIME, Order.DES.equals(queryOrder) ? SortOrder.DESC : SortOrder.ASC);
+        sourceBuilder.size(condition.getSize());
+
+        return sourceBuilder;
+    }
+
+    protected org.apache.skywalking.oap.server.core.query.type.event.Event parseSearchHit(final SearchHit searchHit) {
+        final org.apache.skywalking.oap.server.core.query.type.event.Event event = new org.apache.skywalking.oap.server.core.query.type.event.Event();
+
+        event.setUuid((String) searchHit.getSourceAsMap().get(Event.UUID));
+
+        String service = searchHit.getSourceAsMap().getOrDefault(Event.SERVICE, "").toString();
+        String serviceInstance = searchHit.getSourceAsMap().getOrDefault(Event.SERVICE_INSTANCE, "").toString();
+        String endpoint = searchHit.getSourceAsMap().getOrDefault(Event.ENDPOINT, "").toString();
+        event.setSource(new Source(service, serviceInstance, endpoint));
+
+        event.setName((String) searchHit.getSourceAsMap().get(Event.NAME));
+        event.setType(EventType.parse(searchHit.getSourceAsMap().get(Event.TYPE).toString()));
+        event.setMessage((String) searchHit.getSourceAsMap().get(Event.MESSAGE));
+        event.setParameters((String) searchHit.getSourceAsMap().get(Event.PARAMETERS));
+        event.setStartTime(Long.parseLong(searchHit.getSourceAsMap().get(Event.START_TIME).toString()));
+        String endTimeStr = searchHit.getSourceAsMap().getOrDefault(Event.END_TIME, "0").toString();
+        if (!endTimeStr.isEmpty() && !Objects.equals(endTimeStr, "0")) {
+            event.setEndTime(Long.parseLong(endTimeStr));
+        }
+
+        return event;
+    }
+}
diff --git a/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/StorageModuleElasticsearch7Provider.java b/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/StorageModuleElasticsearch7Provider.java
index 0f7594c..727d58b 100644
--- a/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/StorageModuleElasticsearch7Provider.java
+++ b/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/StorageModuleElasticsearch7Provider.java
@@ -41,6 +41,7 @@ import org.apache.skywalking.oap.server.core.storage.profile.IProfileThreadSnaps
 import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IAlarmQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IBrowserLogQueryDAO;
+import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.ILogQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetadataQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
@@ -68,6 +69,7 @@ import org.apache.skywalking.oap.server.storage.plugin.elasticsearch7.dao.Storag
 import org.apache.skywalking.oap.server.storage.plugin.elasticsearch7.query.AggregationQueryEs7DAO;
 import org.apache.skywalking.oap.server.storage.plugin.elasticsearch7.query.AlarmQueryEs7DAO;
 import org.apache.skywalking.oap.server.storage.plugin.elasticsearch7.query.BrowserLogQueryEs7DAO;
+import org.apache.skywalking.oap.server.storage.plugin.elasticsearch7.query.ES7EventQueryDAO;
 import org.apache.skywalking.oap.server.storage.plugin.elasticsearch7.query.LogQueryEs7DAO;
 import org.apache.skywalking.oap.server.storage.plugin.elasticsearch7.query.MetadataQueryEs7DAO;
 import org.apache.skywalking.oap.server.storage.plugin.elasticsearch7.query.MetricsQueryEs7DAO;
@@ -197,6 +199,8 @@ public class StorageModuleElasticsearch7Provider extends ModuleProvider {
             ));
         this.registerServiceImplementation(
             UITemplateManagementDAO.class, new UITemplateManagementEsDAO(elasticSearch7Client));
+
+        this.registerServiceImplementation(IEventQueryDAO.class, new ES7EventQueryDAO(elasticSearch7Client));
     }
 
     @Override
diff --git a/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/query/ES7EventQueryDAO.java b/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/query/ES7EventQueryDAO.java
new file mode 100644
index 0000000..ee4aba6
--- /dev/null
+++ b/oap-server/server-storage-plugin/storage-elasticsearch7-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/elasticsearch7/query/ES7EventQueryDAO.java
@@ -0,0 +1,50 @@
+/*
+ * 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.oap.server.storage.plugin.elasticsearch7.query;
+
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.skywalking.oap.server.core.event.Event;
+import org.apache.skywalking.oap.server.core.query.type.event.EventQueryCondition;
+import org.apache.skywalking.oap.server.core.query.type.event.Events;
+import org.apache.skywalking.oap.server.library.client.elasticsearch.ElasticSearchClient;
+import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.query.ESEventQueryDAO;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
+
+public class ES7EventQueryDAO extends ESEventQueryDAO {
+    public ES7EventQueryDAO(final ElasticSearchClient client) {
+        super(client);
+    }
+
+    @Override
+    public Events queryEvents(final EventQueryCondition condition) throws Exception {
+        final SearchSourceBuilder sourceBuilder = buildQuery(condition);
+
+        final SearchResponse response = getClient().search(Event.INDEX_NAME, sourceBuilder);
+
+        final Events events = new Events();
+        events.setTotal(response.getHits().getTotalHits().value);
+        events.setEvents(Stream.of(response.getHits().getHits())
+                               .map(this::parseSearchHit)
+                               .collect(Collectors.toList()));
+
+        return events;
+    }
+}
diff --git a/oap-server/server-storage-plugin/storage-influxdb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/influxdb/InfluxStorageProvider.java b/oap-server/server-storage-plugin/storage-influxdb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/influxdb/InfluxStorageProvider.java
index 7b8d5ad..ecdc2ed 100644
--- a/oap-server/server-storage-plugin/storage-influxdb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/influxdb/InfluxStorageProvider.java
+++ b/oap-server/server-storage-plugin/storage-influxdb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/influxdb/InfluxStorageProvider.java
@@ -34,6 +34,7 @@ import org.apache.skywalking.oap.server.core.storage.profile.IProfileThreadSnaps
 import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IAlarmQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IBrowserLogQueryDAO;
+import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.ILogQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetadataQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
@@ -51,6 +52,7 @@ import org.apache.skywalking.oap.server.storage.plugin.influxdb.base.InfluxStora
 import org.apache.skywalking.oap.server.storage.plugin.influxdb.query.AggregationQuery;
 import org.apache.skywalking.oap.server.storage.plugin.influxdb.query.AlarmQuery;
 import org.apache.skywalking.oap.server.storage.plugin.influxdb.query.BrowserLogQuery;
+import org.apache.skywalking.oap.server.storage.plugin.influxdb.query.EventQueryDAO;
 import org.apache.skywalking.oap.server.storage.plugin.influxdb.query.LogQuery;
 import org.apache.skywalking.oap.server.storage.plugin.influxdb.query.MetadataQuery;
 import org.apache.skywalking.oap.server.storage.plugin.influxdb.query.MetricsQuery;
@@ -119,6 +121,8 @@ public class InfluxStorageProvider extends ModuleProvider {
         this.registerServiceImplementation(
             IHistoryDeleteDAO.class, new HistoryDeleteDAO(client));
         this.registerServiceImplementation(UITemplateManagementDAO.class, new UITemplateManagementDAOImpl(client));
+
+        this.registerServiceImplementation(IEventQueryDAO.class, new EventQueryDAO(client));
     }
 
     @Override
diff --git a/oap-server/server-storage-plugin/storage-influxdb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/influxdb/query/EventQueryDAO.java b/oap-server/server-storage-plugin/storage-influxdb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/influxdb/query/EventQueryDAO.java
new file mode 100644
index 0000000..767ebcf
--- /dev/null
+++ b/oap-server/server-storage-plugin/storage-influxdb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/influxdb/query/EventQueryDAO.java
@@ -0,0 +1,164 @@
+/*
+ * 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.oap.server.storage.plugin.influxdb.query;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.oap.server.core.event.Event;
+import org.apache.skywalking.oap.server.core.query.enumeration.Order;
+import org.apache.skywalking.oap.server.core.query.input.Duration;
+import org.apache.skywalking.oap.server.core.query.type.event.EventQueryCondition;
+import org.apache.skywalking.oap.server.core.query.type.event.Events;
+import org.apache.skywalking.oap.server.core.query.type.event.Source;
+import org.apache.skywalking.oap.server.core.query.type.event.EventType;
+import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
+import org.apache.skywalking.oap.server.core.storage.type.StorageDataComplexObject;
+import org.apache.skywalking.oap.server.storage.plugin.influxdb.InfluxClient;
+import org.apache.skywalking.oap.server.storage.plugin.influxdb.InfluxConstants;
+import org.influxdb.dto.Query;
+import org.influxdb.dto.QueryResult;
+import org.influxdb.querybuilder.SelectQueryImpl;
+import org.influxdb.querybuilder.WhereQueryImpl;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.apache.skywalking.oap.server.storage.plugin.influxdb.InfluxConstants.ALL_FIELDS;
+import static org.influxdb.querybuilder.BuiltQuery.QueryBuilder.contains;
+import static org.influxdb.querybuilder.BuiltQuery.QueryBuilder.eq;
+import static org.influxdb.querybuilder.BuiltQuery.QueryBuilder.gt;
+import static org.influxdb.querybuilder.BuiltQuery.QueryBuilder.lt;
+import static org.influxdb.querybuilder.BuiltQuery.QueryBuilder.select;
+
+@Slf4j
+@RequiredArgsConstructor
+public class EventQueryDAO implements IEventQueryDAO {
+    private final InfluxClient client;
+
+    @Override
+    public Events queryEvents(final EventQueryCondition condition) throws Exception {
+        final WhereQueryImpl<SelectQueryImpl> recallQuery = buildQuery(condition);
+
+        final SelectQueryImpl countQuery = select().count(Event.UUID).from(client.getDatabase(), Event.INDEX_NAME);
+        recallQuery.getClauses().forEach(countQuery::where);
+
+        final Query query = new Query(countQuery.getCommand() + recallQuery.getCommand());
+        final List<QueryResult.Result> results = client.query(query);
+        if (log.isDebugEnabled()) {
+            log.debug("SQL: {}", query.getCommand());
+            log.debug("Result: {}", results);
+        }
+        if (results.size() != 2) {
+            throw new IOException("Expecting to get 2 Results, but it is " + results.size());
+        }
+
+        final QueryResult.Series counterSeries = results.get(0).getSeries().get(0);
+        final List<QueryResult.Series> recallSeries = results.get(1).getSeries();
+
+        final Events events = new Events();
+
+        events.setTotal(((Number) counterSeries.getValues().get(0).get(1)).longValue());
+
+        recallSeries.forEach(
+            series -> series.getValues().forEach(
+                values -> events.getEvents().add(parseSeriesValues(series, values))
+            )
+        );
+
+        return events;
+    }
+
+    protected org.apache.skywalking.oap.server.core.query.type.event.Event parseSeriesValues(final QueryResult.Series series, final List<Object> values) {
+        final org.apache.skywalking.oap.server.core.query.type.event.Event event = new org.apache.skywalking.oap.server.core.query.type.event.Event();
+
+        final List<String> columns = series.getColumns();
+        final Map<String, Object> data = new HashMap<>();
+
+        for (int i = 1; i < columns.size(); i++) {
+            Object value = values.get(i);
+            if (value instanceof StorageDataComplexObject) {
+                value = ((StorageDataComplexObject) value).toStorageData();
+            }
+            data.put(columns.get(i), value);
+        }
+        event.setUuid((String) data.get(Event.UUID));
+
+        final String service = (String) data.get(Event.SERVICE);
+        final String serviceInstance = (String) data.get(Event.SERVICE_INSTANCE);
+        final String endpoint = (String) data.get(Event.ENDPOINT);
+
+        event.setSource(new Source(service, serviceInstance, endpoint));
+        event.setName((String) data.get(Event.NAME));
+        event.setType(EventType.parse((String) data.get(Event.TYPE)));
+        event.setMessage((String) data.get(Event.MESSAGE));
+        event.setParameters((String) data.get(Event.PARAMETERS));
+        event.setStartTime(((Number) data.get(Event.START_TIME)).longValue());
+        event.setEndTime(((Number) data.get(Event.END_TIME)).longValue());
+
+        return event;
+    }
+
+    protected WhereQueryImpl<SelectQueryImpl> buildQuery(final EventQueryCondition condition) {
+        final String topFunc = Order.DES.equals(condition.getOrder()) ? InfluxConstants.SORT_DES : InfluxConstants.SORT_ASC;
+        final WhereQueryImpl<SelectQueryImpl> query =
+            select().raw(ALL_FIELDS)
+                    .function(topFunc, Event.START_TIME, condition.getSize())
+                    .from(client.getDatabase(), Event.INDEX_NAME)
+                    .where();
+
+        if (!isNullOrEmpty(condition.getUuid())) {
+            query.and(eq(Event.UUID, condition.getUuid()));
+        }
+
+        final Source source = condition.getSource();
+        if (source != null) {
+            if (!isNullOrEmpty(source.getService())) {
+                query.and(eq(Event.SERVICE, source.getService()));
+            }
+            if (!isNullOrEmpty(source.getServiceInstance())) {
+                query.and(eq(Event.SERVICE_INSTANCE, source.getServiceInstance()));
+            }
+            if (!isNullOrEmpty(source.getEndpoint())) {
+                query.and(contains(Event.ENDPOINT, source.getEndpoint().replaceAll("/", "\\\\/")));
+            }
+        }
+
+        if (!isNullOrEmpty(condition.getName())) {
+            query.and(eq(InfluxConstants.NAME, condition.getName()));
+        }
+
+        if (condition.getType() != null) {
+            query.and(eq(Event.TYPE, condition.getType().name()));
+        }
+
+        final Duration startTime = condition.getTime();
+        if (startTime != null) {
+            if (startTime.getStartTimestamp() > 0) {
+                query.and(gt(Event.START_TIME, startTime.getStartTimestamp()));
+            }
+            if (startTime.getEndTimestamp() > 0) {
+                query.and(lt(Event.END_TIME, startTime.getEndTimestamp()));
+            }
+        }
+
+        return query;
+    }
+}
diff --git a/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/H2StorageProvider.java b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/H2StorageProvider.java
index e4c281a..63fa518 100644
--- a/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/H2StorageProvider.java
+++ b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/H2StorageProvider.java
@@ -37,6 +37,7 @@ import org.apache.skywalking.oap.server.core.storage.profile.IProfileThreadSnaps
 import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IAlarmQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IBrowserLogQueryDAO;
+import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.ILogQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetadataQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
@@ -53,6 +54,7 @@ import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2Aggregation
 import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2AlarmQueryDAO;
 import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2BatchDAO;
 import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2BrowserLogQueryDAO;
+import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2EventQueryDAO;
 import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2HistoryDeleteDAO;
 import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2LogQueryDAO;
 import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2MetadataQueryDAO;
@@ -154,6 +156,8 @@ public class H2StorageProvider extends ModuleProvider {
         this.registerServiceImplementation(
             IProfileThreadSnapshotQueryDAO.class, new H2ProfileThreadSnapshotQueryDAO(h2Client));
         this.registerServiceImplementation(UITemplateManagementDAO.class, new H2UITemplateManagementDAO(h2Client));
+
+        this.registerServiceImplementation(IEventQueryDAO.class, new H2EventQueryDAO(h2Client));
     }
 
     @Override
diff --git a/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2EventQueryDAO.java b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2EventQueryDAO.java
new file mode 100644
index 0000000..865b2e6
--- /dev/null
+++ b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/h2/dao/H2EventQueryDAO.java
@@ -0,0 +1,142 @@
+/*
+ * 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.oap.server.storage.plugin.jdbc.h2.dao;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.oap.server.core.event.Event;
+import org.apache.skywalking.oap.server.core.query.input.Duration;
+import org.apache.skywalking.oap.server.core.query.type.event.EventQueryCondition;
+import org.apache.skywalking.oap.server.core.query.type.event.Events;
+import org.apache.skywalking.oap.server.core.query.type.event.Source;
+import org.apache.skywalking.oap.server.core.query.type.event.EventType;
+import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
+import org.apache.skywalking.oap.server.library.client.jdbc.hikaricp.JDBCHikariCPClient;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+@Slf4j
+@RequiredArgsConstructor
+public class H2EventQueryDAO implements IEventQueryDAO {
+    private final JDBCHikariCPClient client;
+
+    @Override
+    public Events queryEvents(final EventQueryCondition condition) throws Exception {
+        final List<String> conditions = new ArrayList<>();
+        final List<Object> parameters = new ArrayList<>();
+
+        if (!isNullOrEmpty(condition.getUuid())) {
+            conditions.add(Event.UUID + "=?");
+            parameters.add(condition.getUuid());
+        }
+
+        final Source source = condition.getSource();
+        if (source != null) {
+            if (!isNullOrEmpty(source.getService())) {
+                conditions.add(Event.SERVICE + "=?");
+                parameters.add(source.getService());
+            }
+            if (!isNullOrEmpty(source.getServiceInstance())) {
+                conditions.add(Event.SERVICE_INSTANCE + "=?");
+                parameters.add(source.getServiceInstance());
+            }
+            if (!isNullOrEmpty(source.getEndpoint())) {
+                conditions.add(Event.ENDPOINT + "=?");
+                parameters.add(source.getEndpoint());
+            }
+        }
+
+        if (!isNullOrEmpty(condition.getName())) {
+            conditions.add(Event.NAME + "=?");
+            parameters.add(condition.getName());
+        }
+
+        if (condition.getType() != null) {
+            conditions.add(Event.TYPE + "=?");
+            parameters.add(condition.getType().name());
+        }
+
+        final Duration time = condition.getTime();
+        if (time != null) {
+            if (time.getStartTimestamp() > 0) {
+                conditions.add(Event.START_TIME + ">?");
+                parameters.add(time.getStartTimestamp());
+            }
+            if (time.getEndTimestamp() > 0) {
+                conditions.add(Event.END_TIME + "<?");
+                parameters.add(time.getEndTimestamp());
+            }
+        }
+
+        final String whereClause = conditions.isEmpty() ? "" : conditions.stream().collect(Collectors.joining(" and ", " where ", ""));
+
+        final Events result = new Events();
+
+        try (final Connection connection = client.getConnection()) {
+            String sql = "select count(1) total from " + Event.INDEX_NAME + whereClause;
+            if (log.isDebugEnabled()) {
+                log.debug("Count SQL: {}, parameters: {}", sql, parameters);
+            }
+            try (final ResultSet resultSet = client.executeQuery(connection, sql, parameters.toArray())) {
+                if (!resultSet.next()) {
+                    return result;
+                }
+                result.setTotal(resultSet.getInt("total"));
+            }
+
+            sql = "select * from " + Event.INDEX_NAME + whereClause + " limit " + condition.getSize();
+            if (log.isDebugEnabled()) {
+                log.debug("Query SQL: {}, parameters: {}", sql, parameters);
+            }
+            try (final ResultSet resultSet = client.executeQuery(connection, sql, parameters.toArray())) {
+                while (resultSet.next()) {
+                    result.getEvents().add(parseResultSet(resultSet));
+                }
+            }
+        }
+
+        return result;
+    }
+
+    protected org.apache.skywalking.oap.server.core.query.type.event.Event parseResultSet(final ResultSet resultSet) throws SQLException {
+        final org.apache.skywalking.oap.server.core.query.type.event.Event event = new org.apache.skywalking.oap.server.core.query.type.event.Event();
+
+        event.setUuid(resultSet.getString(Event.UUID));
+
+        final String service = resultSet.getString(Event.SERVICE);
+        final String serviceInstance = resultSet.getString(Event.SERVICE_INSTANCE);
+        final String endpoint = resultSet.getString(Event.ENDPOINT);
+
+        event.setSource(new Source(service, serviceInstance, endpoint));
+        event.setName(resultSet.getString(Event.NAME));
+        event.setType(EventType.parse(resultSet.getString(Event.TYPE)));
+        event.setMessage(resultSet.getString(Event.MESSAGE));
+        event.setParameters(resultSet.getString(Event.PARAMETERS));
+        event.setStartTime(resultSet.getLong(Event.START_TIME));
+        event.setEndTime(resultSet.getLong(Event.END_TIME));
+
+        return event;
+    }
+}
diff --git a/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/mysql/MySQLStorageProvider.java b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/mysql/MySQLStorageProvider.java
index 225a0da..c1a9315 100644
--- a/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/mysql/MySQLStorageProvider.java
+++ b/oap-server/server-storage-plugin/storage-jdbc-hikaricp-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/mysql/MySQLStorageProvider.java
@@ -36,6 +36,7 @@ import org.apache.skywalking.oap.server.core.storage.profile.IProfileThreadSnaps
 import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IAlarmQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IBrowserLogQueryDAO;
+import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.ILogQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetadataQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
@@ -49,6 +50,7 @@ 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.oap.server.storage.plugin.jdbc.h2.dao.H2BatchDAO;
+import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2EventQueryDAO;
 import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2HistoryDeleteDAO;
 import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2MetadataQueryDAO;
 import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2MetricsQueryDAO;
@@ -141,6 +143,8 @@ public class MySQLStorageProvider extends ModuleProvider {
         this.registerServiceImplementation(
             IProfileThreadSnapshotQueryDAO.class, new H2ProfileThreadSnapshotQueryDAO(mysqlClient));
         this.registerServiceImplementation(UITemplateManagementDAO.class, new H2UITemplateManagementDAO(mysqlClient));
+
+        this.registerServiceImplementation(IEventQueryDAO.class, new H2EventQueryDAO(mysqlClient));
     }
 
     @Override
diff --git a/oap-server/server-storage-plugin/storage-tidb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/tidb/TiDBStorageProvider.java b/oap-server/server-storage-plugin/storage-tidb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/tidb/TiDBStorageProvider.java
index 5d9557f..aada597 100644
--- a/oap-server/server-storage-plugin/storage-tidb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/tidb/TiDBStorageProvider.java
+++ b/oap-server/server-storage-plugin/storage-tidb-plugin/src/main/java/org/apache/skywalking/oap/server/storage/plugin/jdbc/tidb/TiDBStorageProvider.java
@@ -36,6 +36,7 @@ import org.apache.skywalking.oap.server.core.storage.profile.IProfileThreadSnaps
 import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IAlarmQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IBrowserLogQueryDAO;
+import org.apache.skywalking.oap.server.core.storage.query.IEventQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.ILogQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetadataQueryDAO;
 import org.apache.skywalking.oap.server.core.storage.query.IMetricsQueryDAO;
@@ -49,6 +50,7 @@ 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.oap.server.storage.plugin.jdbc.h2.dao.H2BatchDAO;
+import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2EventQueryDAO;
 import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2HistoryDeleteDAO;
 import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2MetadataQueryDAO;
 import org.apache.skywalking.oap.server.storage.plugin.jdbc.h2.dao.H2MetricsQueryDAO;
@@ -146,6 +148,8 @@ public class TiDBStorageProvider extends ModuleProvider {
         this.registerServiceImplementation(UITemplateManagementDAO.class, new H2UITemplateManagementDAO(mysqlClient));
 
         this.registerServiceImplementation(IHistoryDeleteDAO.class, new TiDBHistoryDeleteDAO(mysqlClient));
+
+        this.registerServiceImplementation(IEventQueryDAO.class, new H2EventQueryDAO(mysqlClient));
     }
 
     @Override
diff --git a/oap-server/server-tools/profile-exporter/tool-profile-snapshot-server-mock/src/main/java/org/apache/skywalking/oap/server/tool/profile/core/MockCoreModuleProvider.java b/oap-server/server-tools/profile-exporter/tool-profile-snapshot-server-mock/src/main/java/org/apache/skywalking/oap/server/tool/profile/core/MockCoreModuleProvider.java
index abd9839..7507a41 100755
--- a/oap-server/server-tools/profile-exporter/tool-profile-snapshot-server-mock/src/main/java/org/apache/skywalking/oap/server/tool/profile/core/MockCoreModuleProvider.java
+++ b/oap-server/server-tools/profile-exporter/tool-profile-snapshot-server-mock/src/main/java/org/apache/skywalking/oap/server/tool/profile/core/MockCoreModuleProvider.java
@@ -39,6 +39,7 @@ import org.apache.skywalking.oap.server.core.profile.ProfileTaskMutationService;
 import org.apache.skywalking.oap.server.core.query.AggregationQueryService;
 import org.apache.skywalking.oap.server.core.query.AlarmQueryService;
 import org.apache.skywalking.oap.server.core.query.BrowserLogQueryService;
+import org.apache.skywalking.oap.server.core.query.EventQueryService;
 import org.apache.skywalking.oap.server.core.query.LogQueryService;
 import org.apache.skywalking.oap.server.core.query.MetadataQueryService;
 import org.apache.skywalking.oap.server.core.query.MetricsMetadataQueryService;
@@ -170,6 +171,8 @@ public class MockCoreModuleProvider extends CoreModuleProvider {
 
         // Management
         this.registerServiceImplementation(UITemplateManagementService.class, new UITemplateManagementService(getManager()));
+
+        this.registerServiceImplementation(EventQueryService.class, new EventQueryService(getManager()));
     }
 
     @Override
diff --git a/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/SimpleQueryClient.java b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/SimpleQueryClient.java
index 3f45115..8bfee4e 100644
--- a/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/SimpleQueryClient.java
+++ b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/SimpleQueryClient.java
@@ -32,6 +32,9 @@ import org.apache.skywalking.e2e.alarm.GetAlarmData;
 import org.apache.skywalking.e2e.browser.BrowserErrorLog;
 import org.apache.skywalking.e2e.browser.BrowserErrorLogQuery;
 import org.apache.skywalking.e2e.browser.BrowserErrorLogsData;
+import org.apache.skywalking.e2e.event.Event;
+import org.apache.skywalking.e2e.event.EventData;
+import org.apache.skywalking.e2e.event.EventsQuery;
 import org.apache.skywalking.e2e.log.Log;
 import org.apache.skywalking.e2e.log.LogData;
 import org.apache.skywalking.e2e.log.LogsQuery;
@@ -437,4 +440,22 @@ public class SimpleQueryClient {
         }
         return Objects.requireNonNull(responseEntity.getBody().getData().isSupport());
     }
+
+    public List<Event> events(final EventsQuery query) throws Exception {
+        final URL queryFileUrl = Resources.getResource("events.gql");
+        final String queryString = Resources.readLines(queryFileUrl, StandardCharsets.UTF_8)
+                                            .stream().filter(it -> !it.startsWith("#"))
+                                            .collect(Collectors.joining())
+                                            .replace("{uuid}", query.uuid());
+        LOGGER.info("Query: {}", queryString);
+        final ResponseEntity<GQLResponse<EventData>> responseEntity = restTemplate.exchange(
+            new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
+            new ParameterizedTypeReference<GQLResponse<EventData>>() {
+            }
+        );
+        if (responseEntity.getStatusCode() != HttpStatus.OK) {
+            throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
+        }
+        return Objects.requireNonNull(responseEntity.getBody()).getData().getEvents().getData();
+    }
 }
diff --git a/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/common/KeyValueMatcher.java b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/common/KeyValueMatcher.java
index 265cedf..7ae538b 100644
--- a/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/common/KeyValueMatcher.java
+++ b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/common/KeyValueMatcher.java
@@ -27,7 +27,7 @@ import static java.util.Objects.nonNull;
 
 @Setter
 @Getter
-@ToString(callSuper = true)
+@ToString
 @EqualsAndHashCode(callSuper = true)
 public class KeyValueMatcher extends AbstractMatcher<KeyValue> {
 
diff --git a/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/common/KeyValueMatcher.java b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/Event.java
similarity index 53%
copy from test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/common/KeyValueMatcher.java
copy to test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/Event.java
index 265cedf..375ea72 100644
--- a/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/common/KeyValueMatcher.java
+++ b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/Event.java
@@ -15,32 +15,39 @@
  * limitations under the License.
  */
 
-package org.apache.skywalking.e2e.common;
-
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import lombok.ToString;
-import org.apache.skywalking.e2e.verification.AbstractMatcher;
-
-import static java.util.Objects.nonNull;
-
-@Setter
-@Getter
-@ToString(callSuper = true)
-@EqualsAndHashCode(callSuper = true)
-public class KeyValueMatcher extends AbstractMatcher<KeyValue> {
-
-    private String key;
-    private String value;
-
-    @Override
-    public void verify(final KeyValue keyValue) {
-        if (nonNull(getKey())) {
-            doVerify(getKey(), keyValue.getKey());
-        }
-        if (nonNull(getValue())) {
-            doVerify(getValue(), keyValue.getValue());
-        }
+package org.apache.skywalking.e2e.event;
+
+import java.util.List;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.apache.skywalking.e2e.common.KeyValue;
+
+@Data
+@Accessors(chain = true)
+public class Event {
+    private String uuid;
+
+    private Source source;
+
+    private String name;
+
+    private String type;
+
+    private String message;
+
+    private List<KeyValue> parameters;
+
+    private String startTime;
+
+    private String endTime;
+
+    @Data
+    @Accessors(chain = true)
+    static class Source {
+        private String service;
+
+        private String serviceInstance;
+
+        private String endpoint;
     }
 }
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/EventData.java
similarity index 69%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/EventData.java
index 7475364..6eba6f4 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/EventData.java
@@ -6,26 +6,28 @@
  * (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
+ *      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.oap.server.core;
-
-import org.junit.Assert;
-import org.junit.Test;
+package org.apache.skywalking.e2e.event;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
+import java.util.List;
+import lombok.Data;
+import lombok.experimental.Accessors;
 
-        Assert.assertEquals(32, coreModule.services().length);
+@Data
+public class EventData {
+    @Data
+    @Accessors(chain = true)
+    public static class Events {
+        private List<Event> data;
     }
+
+    private Events events;
 }
diff --git a/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/EventMatcher.java b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/EventMatcher.java
new file mode 100644
index 0000000..b063d90
--- /dev/null
+++ b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/EventMatcher.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.skywalking.e2e.event;
+
+import java.util.List;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.apache.skywalking.e2e.common.KeyValue;
+import org.apache.skywalking.e2e.common.KeyValueMatcher;
+import org.apache.skywalking.e2e.verification.AbstractMatcher;
+
+import static java.util.Objects.nonNull;
+import static org.assertj.core.api.Assertions.fail;
+
+@Setter
+@Getter
+@ToString(callSuper = true)
+@EqualsAndHashCode(callSuper = true)
+public class EventMatcher extends AbstractMatcher<Event> {
+    private String uuid;
+
+    private Source source;
+
+    private String name;
+
+    private String type;
+
+    private String message;
+
+    private List<KeyValueMatcher> parameters;
+
+    private String startTime;
+
+    private String endTime;
+
+    @Override
+    public void verify(final Event event) {
+        if (nonNull(getUuid())) {
+            doVerify(getUuid(), event.getUuid());
+        }
+        if (nonNull(getSource())) {
+            if (nonNull(getSource().getService())) {
+                doVerify(getSource().getService(), event.getSource().getService());
+            }
+            if (nonNull(getSource().getServiceInstance())) {
+                doVerify(getSource().getServiceInstance(), event.getSource().getServiceInstance());
+            }
+            if (nonNull(getSource().getEndpoint())) {
+                doVerify(getSource().getEndpoint(), event.getSource().getEndpoint());
+            }
+        }
+        if (nonNull(getName())) {
+            doVerify(getName(), event.getName());
+        }
+        if (nonNull(getType())) {
+            doVerify(getType(), event.getType());
+        }
+        if (nonNull(getMessage())) {
+            doVerify(getMessage(), event.getMessage());
+        }
+        if (nonNull(getStartTime())) {
+            doVerify(getStartTime(), event.getStartTime());
+        }
+        if (nonNull(getEndTime())) {
+            doVerify(getEndTime(), event.getEndTime());
+        }
+        if (nonNull(getParameters())) {
+            for (final KeyValueMatcher matcher : getParameters()) {
+                boolean matched = false;
+                for (final KeyValue keyValue : event.getParameters()) {
+                    try {
+                        matcher.verify(keyValue);
+                        matched = true;
+                    } catch (Throwable ignore) {
+
+                    }
+                }
+                if (!matched) {
+                    fail("\nExpected: %s\n Actual: %s", getParameters(), event.getParameters());
+                }
+            }
+        }
+    }
+
+    @Getter
+    @Setter
+    @ToString
+    public static class Source {
+        private String service;
+
+        private String serviceInstance;
+
+        private String endpoint;
+    }
+}
diff --git a/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/EventsMatcher.java b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/EventsMatcher.java
new file mode 100644
index 0000000..06c1322
--- /dev/null
+++ b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/EventsMatcher.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.e2e.event;
+
+import java.util.LinkedList;
+import java.util.List;
+import lombok.Data;
+
+import static org.assertj.core.api.Assertions.fail;
+
+@Data
+public class EventsMatcher {
+
+    private List<EventMatcher> events;
+
+    public EventsMatcher() {
+        this.events = new LinkedList<>();
+    }
+
+    public void verifyLoosely(final List<Event> events) {
+        for (final EventMatcher matcher : getEvents()) {
+            boolean matched = false;
+            for (final Event log : events) {
+                try {
+                    matcher.verify(log);
+                    matched = true;
+                } catch (Throwable e) {
+                    // ignore
+                }
+            }
+            if (!matched) {
+                fail("\nExpected: %s\n Actual: %s", getEvents(), events);
+            }
+        }
+    }
+}
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/EventsQuery.java
similarity index 68%
copy from oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
copy to test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/EventsQuery.java
index 7475364..c5e1964 100644
--- a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/CoreModuleTest.java
+++ b/test/e2e/e2e-data/src/main/java/org/apache/skywalking/e2e/event/EventsQuery.java
@@ -6,26 +6,25 @@
  * (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
+ *      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.oap.server.core;
-
-import org.junit.Assert;
-import org.junit.Test;
+package org.apache.skywalking.e2e.event;
 
-public class CoreModuleTest {
-    @Test
-    public void testOpenServiceList() {
-        CoreModule coreModule = new CoreModule();
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+import org.apache.skywalking.e2e.AbstractQuery;
 
-        Assert.assertEquals(32, coreModule.services().length);
-    }
+@Getter
+@Setter
+@Accessors(fluent = true)
+public class EventsQuery extends AbstractQuery<EventsQuery> {
+    private String uuid;
 }
diff --git a/test/e2e/e2e-data/src/main/resources/events.gql b/test/e2e/e2e-data/src/main/resources/events.gql
new file mode 100644
index 0000000..1589392
--- /dev/null
+++ b/test/e2e/e2e-data/src/main/resources/events.gql
@@ -0,0 +1,39 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+    "query": "query queryEvents($condition: EventQueryCondition) {
+        events: queryEvents(condition: $condition) {
+            data: events {
+                name
+                source {
+                    service serviceInstance endpoint
+                }
+                startTime
+                endTime
+                message
+                parameters {
+                    key value
+                }
+                uuid
+            }
+        }
+    }",
+    "variables": {
+        "condition": {
+            "uuid": "{uuid}"
+        }
+    }
+}
diff --git a/test/e2e/e2e-test/docker/Dockerfile.cli b/test/e2e/e2e-test/docker/Dockerfile.cli
new file mode 100644
index 0000000..1071870
--- /dev/null
+++ b/test/e2e/e2e-test/docker/Dockerfile.cli
@@ -0,0 +1,33 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+FROM golang:1.14 AS builder
+
+ARG COMMIT_HASH=ae085deb32434a0859e49c45819fed7a5c4010b5
+ARG CLI_CODE=${COMMIT_HASH}.tar.gz
+ARG CLI_CODE_URL=https://github.com/apache/skywalking-cli/archive/${CLI_CODE}
+
+ENV CGO_ENABLED=0
+ENV GO111MODULE=on
+
+WORKDIR /cli
+
+ADD ${CLI_CODE_URL} .
+RUN tar -xf ${CLI_CODE} --strip 1
+RUN rm ${CLI_CODE}
+
+RUN make build && mv bin/swctl-latest-linux-amd64 /swctl
+
+ENTRYPOINT [ "/swctl" ]
diff --git a/test/e2e/e2e-test/docker/event/docker-compose.es6.yml b/test/e2e/e2e-test/docker/event/docker-compose.es6.yml
new file mode 100644
index 0000000..9cc21be
--- /dev/null
+++ b/test/e2e/e2e-test/docker/event/docker-compose.es6.yml
@@ -0,0 +1,47 @@
+# 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.
+
+version: '2.1'
+
+services:
+  es:
+    image: elastic/elasticsearch:6.3.2
+    expose:
+      - 9200
+    networks:
+      - e2e
+    environment:
+      - discovery.type=single-node
+      - cluster.routing.allocation.disk.threshold_enabled=false
+    healthcheck:
+      test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/9200"]
+      interval: 5s
+      timeout: 60s
+      retries: 120
+
+  oap:
+    extends:
+      file: ../base-compose.yml
+      service: oap
+    environment:
+      SW_STORAGE: elasticsearch
+      SW_PROMETHEUS_FETCHER: "default"
+      SW_TELEMETRY: prometheus
+    depends_on:
+      es:
+        condition: service_healthy
+
+networks:
+  e2e:
diff --git a/test/e2e/e2e-test/docker/event/docker-compose.es7.0.yml b/test/e2e/e2e-test/docker/event/docker-compose.es7.0.yml
new file mode 100644
index 0000000..113f5d8
--- /dev/null
+++ b/test/e2e/e2e-test/docker/event/docker-compose.es7.0.yml
@@ -0,0 +1,47 @@
+# 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.
+
+version: '2.1'
+
+services:
+  es:
+    image: elastic/elasticsearch:7.0.0
+    expose:
+      - 9200
+    networks:
+      - e2e
+    environment:
+      - discovery.type=single-node
+      - cluster.routing.allocation.disk.threshold_enabled=false
+    healthcheck:
+      test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/9200"]
+      interval: 5s
+      timeout: 60s
+      retries: 120
+
+  oap:
+    extends:
+      file: ../base-compose.yml
+      service: oap-es7
+    environment:
+      SW_STORAGE: elasticsearch7
+      SW_PROMETHEUS_FETCHER: "default"
+      SW_TELEMETRY: prometheus
+    depends_on:
+      es:
+        condition: service_healthy
+
+networks:
+  e2e:
diff --git a/test/e2e/e2e-test/docker/event/docker-compose.h2.yml b/test/e2e/e2e-test/docker/event/docker-compose.h2.yml
new file mode 100644
index 0000000..7d21486
--- /dev/null
+++ b/test/e2e/e2e-test/docker/event/docker-compose.h2.yml
@@ -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.
+
+version: '2.1'
+
+services:
+  h2db:
+    build:
+      context: .
+      dockerfile: ../Dockerfile.h2
+    networks:
+      - e2e
+    expose:
+      - 1521
+    healthcheck:
+      test: ["CMD", "sh", "-c", "nc -z 127.0.0.1 1521"]
+      interval: 5s
+      timeout: 60s
+      retries: 120
+
+  oap:
+    extends:
+      file: ../base-compose.yml
+      service: oap
+    environment:
+      SW_STORAGE: h2
+      SW_STORAGE_H2_URL: jdbc:h2:tcp://h2db:1521/skywalking-oap-db
+    depends_on:
+      h2db:
+        condition: service_healthy
+
+networks:
+  e2e:
\ No newline at end of file
diff --git a/test/e2e/e2e-test/docker/event/docker-compose.influxdb.yml b/test/e2e/e2e-test/docker/event/docker-compose.influxdb.yml
new file mode 100644
index 0000000..6fe848c
--- /dev/null
+++ b/test/e2e/e2e-test/docker/event/docker-compose.influxdb.yml
@@ -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.
+
+version: '2.1'
+
+services:
+  influxdb:
+    image: influxdb:1.7.9
+    expose:
+      - 8086
+    networks:
+      - e2e
+    healthcheck:
+      test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/8086"]
+      interval: 5s
+      timeout: 60s
+      retries: 120
+
+  oap:
+    extends:
+      file: ../base-compose.yml
+      service: oap
+    environment:
+      SW_STORAGE: influxdb
+      SW_PROMETHEUS_FETCHER: "default"
+      SW_TELEMETRY: prometheus
+    depends_on:
+      influxdb:
+        condition: service_healthy
+
+networks:
+  e2e:
diff --git a/test/e2e/e2e-test/docker/event/docker-compose.mysql.yml b/test/e2e/e2e-test/docker/event/docker-compose.mysql.yml
new file mode 100644
index 0000000..303b0a2
--- /dev/null
+++ b/test/e2e/e2e-test/docker/event/docker-compose.mysql.yml
@@ -0,0 +1,49 @@
+# 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.
+
+version: '2.1'
+
+services:
+  mysql:
+    image: mysql/mysql-server:8.0.13
+    networks:
+      - e2e
+    expose:
+      - 3306
+    environment:
+      - MYSQL_ROOT_PASSWORD=root@1234
+      - MYSQL_DATABASE=swtest
+      - MYSQL_ROOT_HOST=%
+    healthcheck:
+      test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/3306"]
+      interval: 5s
+      timeout: 60s
+      retries: 120
+
+  oap:
+    extends:
+      file: ../base-compose.yml
+      service: oap
+    environment:
+      SW_STORAGE: mysql
+      SW_PROMETHEUS_FETCHER: "default"
+      SW_TELEMETRY: prometheus
+    depends_on:
+      mysql:
+        condition: service_healthy
+    entrypoint: ['sh', '-c', '/download-mysql.sh && /skywalking/docker-entrypoint.sh']
+
+networks:
+  e2e:
diff --git a/test/e2e/e2e-test/docker/event/docker-compose.yml b/test/e2e/e2e-test/docker/event/docker-compose.yml
new file mode 100644
index 0000000..8126957
--- /dev/null
+++ b/test/e2e/e2e-test/docker/event/docker-compose.yml
@@ -0,0 +1,56 @@
+# 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.
+
+version: '3.8'
+
+services:
+  ui:
+    extends:
+      file: ../base-compose.yml
+      service: ui
+    depends_on:
+      oap:
+        condition: service_healthy
+
+  cli:
+    build:
+      context: .
+      dockerfile: ../Dockerfile.cli
+    networks:
+      - e2e
+    entrypoint:
+      - /bin/sh
+      - -c
+    command:
+      - >-
+        /swctl
+        --grpcAddr=oap:11800
+        event report
+        --uuid=abcde
+        --name=Upgrade
+        --service=e2e-service
+        --instance=e2e-service-instance
+        --endpoint='/e2e-endpoint'
+        --message='Upgrade from {fromVersion} to {toVersion}'
+        --startTime=`date +%s`000
+        --endTime=`date +%s`999
+        fromVersion=v1
+        toVersion=v2
+    depends_on:
+      oap:
+        condition: service_healthy
+
+networks:
+  e2e:
diff --git a/test/e2e/e2e-test/src/test/java/org/apache/skywalking/e2e/event/EventE2E.java b/test/e2e/e2e-test/src/test/java/org/apache/skywalking/e2e/event/EventE2E.java
new file mode 100644
index 0000000..64d2b8c
--- /dev/null
+++ b/test/e2e/e2e-test/src/test/java/org/apache/skywalking/e2e/event/EventE2E.java
@@ -0,0 +1,66 @@
+/*
+ * 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.e2e.event;
+
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.e2e.ProfileClient;
+import org.apache.skywalking.e2e.annotation.ContainerHostAndPort;
+import org.apache.skywalking.e2e.annotation.DockerCompose;
+import org.apache.skywalking.e2e.base.SkyWalkingE2E;
+import org.apache.skywalking.e2e.base.SkyWalkingTestAdapter;
+import org.apache.skywalking.e2e.common.HostAndPort;
+import org.apache.skywalking.e2e.retryable.RetryableTest;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.containers.DockerComposeContainer;
+
+import static org.apache.skywalking.e2e.utils.Times.now;
+import static org.apache.skywalking.e2e.utils.Yamls.load;
+
+@Slf4j
+@SkyWalkingE2E
+public class EventE2E extends SkyWalkingTestAdapter {
+    @SuppressWarnings("unused")
+    @DockerCompose({
+        "docker/event/docker-compose.yml",
+        "docker/event/docker-compose.${SW_STORAGE}.yml",
+    })
+    protected DockerComposeContainer<?> compose;
+
+    @SuppressWarnings("unused")
+    @ContainerHostAndPort(name = "ui", port = 8080)
+    protected HostAndPort swWebappHostPort;
+
+    private ProfileClient graphql;
+
+    @BeforeAll
+    public void setUp() {
+        graphql = new ProfileClient(swWebappHostPort.host(), swWebappHostPort.port());
+    }
+
+    @RetryableTest
+    void events() throws Exception {
+        final List<Event> events = graphql.events(new EventsQuery().start(startTime).end(now()).uuid("abcde"));
+
+        LOGGER.info("events: {}", events);
+
+        load("expected/event/events.yml").as(EventsMatcher.class).verifyLoosely(events);
+    }
+
+}
diff --git a/test/e2e/e2e-test/src/test/resources/expected/event/events.yml b/test/e2e/e2e-test/src/test/resources/expected/event/events.yml
new file mode 100644
index 0000000..59baeb1
--- /dev/null
+++ b/test/e2e/e2e-test/src/test/resources/expected/event/events.yml
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+events:
+  - name: Upgrade
+    source:
+      service: e2e-service
+      serviceInstance: e2e-service-instance
+      endpoint: /e2e-endpoint
+    startTime: gt 0
+    endTime: gt 0
+    message: Upgrade from {fromVersion} to {toVersion}
+    parameters:
+      - key: fromVersion
+        value: v1
+      - key: toVersion
+        value: v2
+    uuid: abcde