feat: Implement the Log Analysis Language text regexp debugger (#536)

diff --git a/package-lock.json b/package-lock.json
index 603d6c5..2ba8688 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6642,12 +6642,12 @@
       "dev": true
     },
     "json5": {
-      "version": "2.1.0",
-      "resolved": "http://registry.npm.taobao.org/json5/download/json5-2.1.0.tgz",
-      "integrity": "sha1-56DGLEgoXGKNIKELhcibuAfDKFA=",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
+      "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
       "dev": true,
       "requires": {
-        "minimist": "^1.2.0"
+        "minimist": "^1.2.5"
       }
     },
     "jsonfile": {
@@ -7602,9 +7602,9 @@
       }
     },
     "minimist": {
-      "version": "1.2.0",
-      "resolved": "http://registry.npm.taobao.org/minimist/download/minimist-1.2.0.tgz",
-      "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
       "dev": true
     },
     "mississippi": {
@@ -7669,6 +7669,39 @@
         }
       }
     },
+    "monaco-editor": {
+      "version": "0.27.0",
+      "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.27.0.tgz",
+      "integrity": "sha512-UhwP78Wb8w0ZSYoKXQNTV/0CHObp6NS3nCt51QfKE6sKyBo5PBsvuDOHoI2ooBakc6uIwByRLHVeT7+yXQe2fQ=="
+    },
+    "monaco-editor-webpack-plugin": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-4.1.2.tgz",
+      "integrity": "sha512-snmHecygICKT0UlHhva+Cs2WaLPpxy3111xbvInhjjTr5m0xQTFHlmJ2QQDcB14Vzmm7f07uc1TtbvOpmL50BA==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^2.0.0"
+      },
+      "dependencies": {
+        "emojis-list": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+          "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+          "dev": true
+        },
+        "loader-utils": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
+          "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
+          "dev": true,
+          "requires": {
+            "big.js": "^5.2.2",
+            "emojis-list": "^3.0.0",
+            "json5": "^2.1.2"
+          }
+        }
+      }
+    },
     "move-concurrently": {
       "version": "1.0.1",
       "resolved": "http://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz",
@@ -9629,15 +9662,6 @@
           "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
           "dev": true
         },
-        "json5": {
-          "version": "2.1.3",
-          "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
-          "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.5"
-          }
-        },
         "loader-utils": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
@@ -9649,12 +9673,6 @@
             "json5": "^2.1.2"
           }
         },
-        "minimist": {
-          "version": "1.2.5",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-          "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
-          "dev": true
-        },
         "neo-async": {
           "version": "2.6.2",
           "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
diff --git a/package.json b/package.json
index acf78b9..b0fb897 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
     "dayjs": "^1.8.8",
     "echarts": "^5.0.2",
     "lodash": "^4.17.15",
+    "monaco-editor": "^0.27.0",
     "noty": "^3.2.0-beta",
     "popper.js": "^1.14.7",
     "vue": "^2.6.6",
@@ -45,6 +46,7 @@
     "@vue/cli-service": "^3.4.1",
     "husky": "^4.0.9",
     "lint-staged": "^9.5.0",
+    "monaco-editor-webpack-plugin": "^4.1.2",
     "prettier": "^1.19.1",
     "sass": "^1.27.0",
     "sass-loader": "^10.0.4",
diff --git a/src/assets/lang/en.ts b/src/assets/lang/en.ts
index 0e0ced9..6f34e8e 100644
--- a/src/assets/lang/en.ts
+++ b/src/assets/lang/en.ts
@@ -96,7 +96,7 @@
   search: 'Search',
   clear: 'Clear',
   more: 'More',
-  traceID: 'TraceID',
+  traceID: 'Trace ID',
   range: 'Range',
   timeRange: 'Time Range',
   duration: 'Duration',
@@ -232,6 +232,23 @@
   eventSource: 'Event Source',
   modalTitle: 'Inspection',
   selectRedirectPage: 'Do you want to inspect Traces or Logs of %s service?',
+  logAnalysis: 'Log Analysis Language',
+  logDataBody: 'The content of the log',
+  addType: 'Please input a type',
+  traceContext: 'Logs with trace context',
+  traceSegmentId: 'Trace Segment ID',
+  spanId: 'Span ID',
+  inputTraceSegmentId: 'Please input the trace segment ID',
+  inputSpanId: 'Please input the span ID',
+  inputTraceId: 'Please input the trace ID',
+  dsl: 'Script input for LAL',
+  logContentType: 'The type of the log content',
+  logRespContent: 'Log Content',
+  analysis: 'Analysis',
+  waitLoading: 'Loading',
+  dslEmpty: 'Script input of LAL should not be empty',
+  logContentEmpty: 'The content of the log should not not be empty.',
+  debug: 'Debug',
 };
 
 export default m;
diff --git a/src/assets/lang/zh.ts b/src/assets/lang/zh.ts
index 5e13722..5957867 100644
--- a/src/assets/lang/zh.ts
+++ b/src/assets/lang/zh.ts
@@ -230,6 +230,23 @@
   eventSource: '事件资源',
   modalTitle: '查看',
   selectRedirectPage: '查看 %s 服务的追踪或日志?',
+  logAnalysis: '日志分析语言',
+  logDataBody: '日志数据的内容',
+  addType: '请输入一个类型',
+  traceContext: '具有跟踪上下文的日志',
+  traceSegmentId: '跟踪段ID',
+  spanId: '跨度ID',
+  inputTraceSegmentId: '请输入跟踪段ID',
+  inputSpanId: '请输入跨度ID',
+  inputTraceId: '请输入跟踪ID',
+  dsl: 'LAL的脚本输入',
+  logContentType: '日志内容的类型',
+  logRespContent: '日志内容',
+  analysis: '分析',
+  waitLoading: '加载中',
+  dslEmpty: 'LAL的脚本输入不应该是空',
+  logContentEmpty: '日志数据的内容不应该是空。',
+  debug: '调试',
 };
 
 export default m;
diff --git a/src/components/rk-date.vue b/src/components/rk-date.vue
index cf9d311..6d8c340 100755
--- a/src/components/rk-date.vue
+++ b/src/components/rk-date.vue
@@ -368,7 +368,6 @@
   }
 
   .datepicker-popup {
-    // right: 0px;
     border-radius: 4px;
     position: absolute;
     transition: all 200ms ease;
@@ -398,7 +397,7 @@
       transform-origin: center top;
     }
     &.right {
-      right: 0;
+      right: -80px;
       top: 30px;
       transform-origin: center top;
     }
diff --git a/src/components/rk-sidebox.vue b/src/components/rk-sidebox.vue
index 5720c86..55b23a6 100644
--- a/src/components/rk-sidebox.vue
+++ b/src/components/rk-sidebox.vue
@@ -103,7 +103,7 @@
     right: 10px;
     top: 0;
     cursor: pointer;
-    color: #d8d8d8;
+    color: #666;
     transition: color 0.3s;
     .icon {
       width: 18px;
diff --git a/src/graph/fragments/debug.ts b/src/graph/fragments/debug.ts
new file mode 100644
index 0000000..155d786
--- /dev/null
+++ b/src/graph/fragments/debug.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export const QueryLogTest = {
+  variable: '$requests: LogTestRequest!',
+  query: `test(requests: $requests) { log { content, serviceName, serviceInstanceName, endpointName,
+    traceId, timestamp, contentType, content, tags { key, value }}
+  metrics { name, value, timestamp, tags { key, value }} }`,
+};
diff --git a/src/graph/fragments/profile.ts b/src/graph/fragments/profile.ts
index f4e2da3..7b5d247 100644
--- a/src/graph/fragments/profile.ts
+++ b/src/graph/fragments/profile.ts
@@ -35,8 +35,10 @@
         key value
       }
       logs {
-        time data {
-            key value
+        time
+        data {
+          key
+          value
         }
       }
     }
diff --git a/src/graph/index.ts b/src/graph/index.ts
index 6e2d922..e6fa409 100644
--- a/src/graph/index.ts
+++ b/src/graph/index.ts
@@ -24,6 +24,7 @@
 import * as profile from './query/profile';
 import * as dashboard from './query/dashboard';
 import * as errorLog from './query/log';
+import * as logDebug from './query/debug';
 
 const query: any = {
   ...errorLog,
@@ -33,6 +34,7 @@
   ...alarm,
   ...profile,
   ...dashboard,
+  ...logDebug,
 };
 
 class Graph {
diff --git a/src/graph/query/debug.ts b/src/graph/query/debug.ts
new file mode 100644
index 0000000..a9ed73c
--- /dev/null
+++ b/src/graph/query/debug.ts
@@ -0,0 +1,20 @@
+/**
+ * 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.
+ */
+
+import { QueryLogTest } from '../fragments/debug';
+
+export const queryLogTest = `query test(${QueryLogTest.variable}) {${QueryLogTest.query}}`;
diff --git a/src/router.ts b/src/router.ts
index 5eff998..a5b1afe 100644
--- a/src/router.ts
+++ b/src/router.ts
@@ -70,6 +70,11 @@
         component: () => import('./views/containers/event.vue'),
         meta: { icon: 'storage', title: 'event', exact: false },
       },
+      {
+        path: 'debug',
+        component: () => import('./views/containers/debug.vue'),
+        meta: { icon: 'library_books', title: 'debug', exact: false },
+      },
     ],
   },
 ];
diff --git a/src/store/index.ts b/src/store/index.ts
index 0610eee..7ad1a68 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -27,6 +27,7 @@
 import profileStore, { State as ProfileState } from '@/store/modules/profile/profile-store';
 import rocketLog, { State as LogState } from '@/store/modules/log';
 import rocketEvent, { State as EventState } from '@/store/modules/event';
+import rocketDebugLAL, { State as logLALState } from '@/store/modules/debug/log-lal';
 
 Vue.use(Vuex);
 
@@ -41,6 +42,7 @@
   profileStore: ProfileState;
   logStore: LogState;
   eventStore: EventState;
+  logLALStore: logLALState;
 }
 
 export default new Vuex.Store({
@@ -55,5 +57,6 @@
     profileStore,
     rocketLog,
     rocketEvent,
+    rocketDebugLAL,
   },
 });
diff --git a/src/store/modules/debug/log-lal.ts b/src/store/modules/debug/log-lal.ts
new file mode 100644
index 0000000..4082170
--- /dev/null
+++ b/src/store/modules/debug/log-lal.ts
@@ -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.
+ */
+
+import { Commit, ActionTree, MutationTree } from 'vuex';
+import * as types from '@/store/mutation-types';
+import { LogTestOptions, LogTestMetrics } from '@/types/debug';
+import graph from '@/graph';
+import { AxiosResponse } from 'axios';
+import { DurationTime, Option } from '@/types/global';
+
+export interface State {
+  logTestFields: LogTestOptions | any;
+  services: Option[];
+  instances: Option[];
+  endpoints: Option[];
+  selectedService: Option;
+  selectedEndpoint: Option;
+  selectedInstance: Option;
+  dsl: string;
+  logTestResp: { log: { content: string }; metrics: LogTestMetrics[] };
+  tabType: string;
+}
+
+const logAnaState: State = {
+  logTestFields: {},
+  services: [],
+  selectedService: { key: '', label: '' },
+  instances: [],
+  selectedInstance: { key: '', label: '' },
+  endpoints: [],
+  selectedEndpoint: { key: '', label: '' },
+  dsl: '',
+  logTestResp: { log: { content: '' }, metrics: [] },
+  tabType: 'LAL',
+};
+
+// mutations
+const mutations: MutationTree<State> = {
+  [types.SET_LOG_TEST_FIELDS](state, item: Option) {
+    state.logTestFields = {
+      ...state.logTestFields,
+      [item.label]: item.key,
+    };
+  },
+  [types.SET_LOG_ANA_SERVICES](state, items: Option[]) {
+    state.services = items;
+    state.selectedService = items[0] || state.selectedService;
+    state.logTestFields.service = state.selectedService.label || undefined;
+  },
+  [types.SET_LOG_ANA_ENDPOINTS](state, items: Option[]) {
+    state.endpoints = [{ key: '', label: 'None' }, ...items];
+    state.selectedEndpoint = state.endpoints[0];
+    state.logTestFields.endpoint = state.selectedEndpoint.key ? state.selectedEndpoint.label : undefined;
+  },
+  [types.SET_LOG_ANA_INSTANCES](state, items: Option[]) {
+    state.instances = [{ key: '', label: 'None' }, ...items];
+    state.selectedInstance = state.instances[0];
+    state.logTestFields.serviceInstance = state.selectedInstance.key ? state.selectedInstance.label : undefined;
+  },
+  [types.SET_SELECTED_SERVICE](state, item: Option) {
+    state.selectedService = item;
+  },
+  [types.SET_SELECTED_ENDPOINT](state, item: Option) {
+    state.selectedEndpoint = item;
+  },
+  [types.SET_SELECTED_INSTANCE](state, item: Option) {
+    state.selectedInstance = item;
+  },
+  [types.SET_DSL](state, content: string) {
+    state.dsl = content;
+  },
+  [types.SET_LOG_TEST_RESPONSE](state, resp: { log: { content: string }; metrics: LogTestMetrics[] }) {
+    state.logTestResp = resp;
+  },
+  [types.SET_TAB_TYPE](state, type: string) {
+    state.tabType = type;
+  },
+};
+
+// actions
+const actions: ActionTree<State, any> = {
+  GET_LOG_ANA_SERVICES(context: { commit: Commit }, params: { duration: DurationTime; keyword: string }) {
+    if (!params.keyword) {
+      params.keyword = '';
+    }
+    return graph
+      .query('queryServices')
+      .params(params)
+      .then((res: AxiosResponse) => {
+        context.commit(types.SET_LOG_ANA_SERVICES, res.data.data.services);
+      });
+  },
+  GET_LOG_ANA_ENDPOINTS(
+    context: { commit: Commit; state: State },
+    params: { keyword: string; currentService?: { key: string; label: string } },
+  ) {
+    if (!context.state.selectedService.key) {
+      context.commit(types.SET_LOG_ANA_ENDPOINTS, []);
+      return;
+    }
+    if (!params.keyword) {
+      params.keyword = '';
+    }
+    return graph
+      .query('queryEndpoints')
+      .params({
+        serviceId: context.state.selectedService.key || '',
+        ...params,
+      })
+      .then((res: AxiosResponse) => {
+        context.commit(types.SET_LOG_ANA_ENDPOINTS, res.data.data.getEndpoints);
+      });
+  },
+  GET_LOG_ANA_INSTANCES(context: { commit: Commit; state: State }, params: any) {
+    if (!context.state.selectedService.key) {
+      context.commit(types.SET_LOG_ANA_INSTANCES, []);
+      return;
+    }
+    return graph
+      .query('queryInstances')
+      .params({ serviceId: context.state.selectedService.key || '', ...params })
+      .then((res: AxiosResponse) => {
+        context.commit(types.SET_LOG_ANA_INSTANCES, res.data.data.getServiceInstances);
+      });
+  },
+  LOG_ANA_QUERY(context: { commit: Commit; state: State }) {
+    const requests = {
+      dsl: context.state.dsl,
+      log: JSON.stringify(context.state.logTestFields),
+    };
+    const params = { requests };
+
+    return graph
+      .query('queryLogTest')
+      .params(params)
+      .then((res: AxiosResponse) => {
+        if (res.data.errors) {
+          context.commit(types.SET_LOG_TEST_RESPONSE, { log: {}, metrics: [] });
+          return res.data.errors;
+        }
+        context.commit(types.SET_LOG_TEST_RESPONSE, res.data.data.test);
+      });
+  },
+};
+
+export default {
+  state: logAnaState,
+  actions,
+  mutations,
+};
diff --git a/src/store/modules/log/index.ts b/src/store/modules/log/index.ts
index 07c437f..2890f1d 100644
--- a/src/store/modules/log/index.ts
+++ b/src/store/modules/log/index.ts
@@ -15,28 +15,24 @@
  * limitations under the License.
  */
 
-import { Commit, ActionTree, MutationTree, Dispatch } from 'vuex';
+import { Commit, ActionTree, MutationTree } from 'vuex';
 import * as types from '@/store/mutation-types';
 import { AxiosResponse } from 'axios';
 import graph from '@/graph';
-
-interface Options {
-  key: string;
-  label: string;
-}
+import { Option } from '@/types/global';
 export interface State {
-  type: Options;
-  logCategories: Options[];
+  type: Option;
+  logCategories: Option[];
   logs: any[];
   total: number;
-  categories: Options[];
-  category: Options;
+  categories: Option[];
+  category: Option;
   loading: boolean;
   conditions: any;
   supportQueryLogsByKeywords: boolean;
 }
 
-const categories: Options[] = [
+const categories: Option[] = [
   { label: 'All', key: 'ALL' },
   { label: 'Ajax', key: 'AJAX' },
   { label: 'Resource', key: 'RESOURCE' },
@@ -71,10 +67,10 @@
 
 // mutations
 const mutations: MutationTree<State> = {
-  [types.SELECT_LOG_TYPE](state: State, data: Options) {
+  [types.SELECT_LOG_TYPE](state: State, data: Option) {
     state.type = data;
   },
-  [types.SELECT_ERROR_CATALOG](state: State, data: Options) {
+  [types.SELECT_ERROR_CATALOG](state: State, data: Option) {
     state.category = data;
   },
   [types.SET_LOGS](state: State, data: any[]) {
@@ -86,7 +82,7 @@
   [types.SET_LOADING](state: State, data: boolean) {
     state.loading = data;
   },
-  [types.SET_LOG_CONDITIONS](state: State, item: Options) {
+  [types.SET_LOG_CONDITIONS](state: State, item: Option) {
     state.conditions = {
       ...state.conditions,
       [item.label]: item.key,
diff --git a/src/store/mutation-types.ts b/src/store/mutation-types.ts
index f7baa67..a348f5e 100644
--- a/src/store/mutation-types.ts
+++ b/src/store/mutation-types.ts
@@ -153,3 +153,15 @@
 // Event
 export const UPDATE_EVENTS = 'UPDATE_EVENTS';
 export const SET_TOTAL_SIZE = 'SET_TOTAL_SIZE';
+
+// debug
+export const SET_TAB_TYPE = 'SET_TAB_TYPE';
+export const SET_LOG_TEST_FIELDS = 'SET_LOG_TEST_FIELDS';
+export const SET_LOG_ANA_SERVICES = 'SET_LOG_ANA_SERVICES';
+export const SET_LOG_ANA_ENDPOINTS = 'SET_LOG_ANA_ENDPOINTS';
+export const SET_LOG_ANA_INSTANCES = 'SET_LOG_ANA_INSTANCES';
+export const SET_SELECTED_INSTANCE = 'SET_SELECTED_INSTANCE';
+export const SET_SELECTED_SERVICE = 'SET_SELECTED_SERVICE';
+export const SET_SELECTED_ENDPOINT = 'SET_SELECTED_ENDPOINT';
+export const SET_DSL = 'SET_DSL';
+export const SET_LOG_TEST_RESPONSE = 'SET_LOG_TEST_RESPONSE';
diff --git a/src/types.d.ts b/src/types.d.ts
index 6f8ac8d..26b79cf 100644
--- a/src/types.d.ts
+++ b/src/types.d.ts
@@ -17,3 +17,4 @@
 
 declare module '*.js';
 declare module 'echarts/lib/echarts';
+declare module 'monaco-editor/esm/vs/editor/editor.main.js';
diff --git a/src/types/debug.d.ts b/src/types/debug.d.ts
new file mode 100644
index 0000000..1fd9396
--- /dev/null
+++ b/src/types/debug.d.ts
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export interface LogTestOptions {
+  service: string;
+  serviceInstance: string;
+  endpoint: string;
+  timestamp: number;
+  body: LogDataBody;
+  traceContext: TraceContext;
+  tags: LogTags;
+}
+
+interface LogDataBody {
+  type: string;
+  content: string;
+}
+
+interface TraceContext {
+  traceId: string;
+  traceSegmentId: string;
+  spanId: number;
+}
+
+interface LogTags {
+  data: { key: string; value: string };
+}
+export interface LogTestMetrics {
+  name: String;
+  tags: { key: string; value: string };
+  value: number;
+  timestamp: number;
+}
diff --git a/src/views/components/common/condition-tags.vue b/src/views/components/common/condition-tags.vue
index b27fc18..f0623bf 100644
--- a/src/views/components/common/condition-tags.vue
+++ b/src/views/components/common/condition-tags.vue
@@ -13,9 +13,9 @@
 See the License for the specific language governing permissions and
 limitations under the License. -->
 <template>
-  <div class="flex-h">
+  <div class="flex-h" :class="{ light: theme === 'light' }">
     <div class="mr-10 pt-5">
-      <span class="sm grey">{{ $t('tags') }}: </span>
+      <span class="sm grey" v-show="theme === 'dark'">{{ $t('tags') }}: </span>
       <span class="rk-trace-tags">
         <span class="selected" v-for="(item, index) in tagsList" :key="index">
           <span>{{ item }}</span>
@@ -41,6 +41,7 @@
   export default class ConditionTags extends Vue {
     @Prop() private type!: string;
     @Prop() private clearTags!: boolean;
+    @Prop({ default: 'dark' }) private theme!: string;
     private tagsList: string[] = [];
     private tags: string = '';
 
@@ -129,4 +130,13 @@
   .tags-tip {
     color: #a7aebb;
   }
+  .light {
+    color: #3d444f;
+    input {
+      border: 1px solid #ccc;
+    }
+    .selected {
+      color: #3d444f;
+    }
+  }
 </style>
diff --git a/src/views/components/debug/debug-constant.ts b/src/views/components/debug/debug-constant.ts
new file mode 100644
index 0000000..2af4458
--- /dev/null
+++ b/src/views/components/debug/debug-constant.ts
@@ -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.
+ */
+
+export enum LogTestConstants {
+  Timestamp = 'timestamp',
+  Service = 'service',
+  ServiceInstance = 'serviceInstance',
+  Endpoint = 'endpoint',
+  Body = 'body',
+  TraceContext = 'traceContext',
+  Tags = 'tags',
+  DSL = 'dsl',
+  Type = 'type',
+}
+
+export const TypeList = [
+  {
+    label: 'Text',
+    value: 'text',
+  },
+  {
+    label: 'Json',
+    value: 'json',
+  },
+  {
+    label: 'YAML',
+    value: 'yaml',
+  },
+];
+
+export const LogMetricsHeader = [
+  {
+    label: 'name',
+    value: 'name',
+  },
+  {
+    label: 'time',
+    value: 'timestamp',
+  },
+  {
+    label: 'value',
+    value: 'value',
+  },
+  {
+    label: 'tags',
+    value: 'tags',
+  },
+];
diff --git a/src/views/components/debug/log-lal.vue b/src/views/components/debug/log-lal.vue
new file mode 100644
index 0000000..d2b4d9b
--- /dev/null
+++ b/src/views/components/debug/log-lal.vue
@@ -0,0 +1,379 @@
+<!-- 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. -->
+
+<template>
+  <div class="log-lal">
+    <div class="lal-conditions">
+      <div class="flex-v">
+        <div>
+          <label>{{ $t('service') }}<b class="require"> *</b></label>
+          <RkSelect
+            class="mb-5"
+            :current="rocketLogLAL.selectedService"
+            :data="rocketLogLAL.services"
+            @onChoose="(item) => changeLogAnaOptions(logTestConstants.Service, item)"
+          />
+        </div>
+        <div>
+          <label>{{ $t('serviceinstance') }}</label>
+          <RkSelect
+            class="mb-5"
+            :current="rocketLogLAL.selectedInstance"
+            :data="rocketLogLAL.instances"
+            @onChoose="(item) => changeLogAnaOptions(logTestConstants.ServiceInstance, item)"
+          />
+        </div>
+        <div>
+          <label>{{ $t('endpoint') }}</label>
+          <RkSelect
+            class="mb-5"
+            :current="rocketLogLAL.selectedEndpoint"
+            :data="rocketLogLAL.endpoints"
+            @onChoose="(item) => changeLogAnaOptions(logTestConstants.Endpoint, item)"
+          />
+        </div>
+        <div>
+          <label>{{ $t('time') }}<b class="require"> *</b></label>
+          <div>
+            <span>
+              <RkDate class="sm" v-model="time" position="right" format="YYYY-MM-DD HH:mm:ss" />
+            </span>
+          </div>
+        </div>
+        <div>
+          <label>{{ $t('tags') }}</label>
+          <ConditionTags :theme="'light'" :clearTags="false" @updateTags="updateTags" />
+        </div>
+        <div>
+          <label>{{ $t('logContentType') }}</label>
+          <RkSelect
+            class="mb-5"
+            :current="currentType"
+            :data="typeList"
+            @onChoose="(item) => changeLogAnaOptions(logTestConstants.Type, item)"
+          />
+        </div>
+        <div class="logDataBody">
+          <label>{{ $t('logDataBody') }}<b class="require"> *</b></label>
+          <textarea v-model="logContent" @change="changeLogAnaOptions(logTestConstants.Body)" />
+        </div>
+        <div>
+          <label>{{ $t('traceID') }}</label>
+          <input
+            type="text"
+            :placeholder="$t('inputTraceId')"
+            v-model="traceID"
+            @change="changeLogAnaOptions(logTestConstants.TraceContext)"
+          />
+        </div>
+        <div>
+          <label>{{ $t('traceSegmentId') }}</label>
+          <input
+            type="text"
+            :placeholder="$t('inputTraceSegmentId')"
+            v-model="segmentID"
+            @change="changeLogAnaOptions(logTestConstants.TraceContext)"
+          />
+        </div>
+        <div>
+          <label>{{ $t('spanId') }}</label>
+          <input
+            type="number"
+            :placeholder="$t('inputSpanId')"
+            v-model="spanID"
+            @change="changeLogAnaOptions(logTestConstants.TraceContext)"
+          />
+        </div>
+        <div>
+          <label>{{ $t('dsl') }}<b class="require"> *</b></label>
+          <div ref="dslContent" id="dsl" contenteditable="true" />
+        </div>
+      </div>
+    </div>
+    <div class="log-analysis">
+      <div class="error-tips">{{ errorCnt }}</div>
+      <div class="log-ana-btn bg-blue" @click="logAnalysis">{{ isLoading ? $t('waitLoading') : $t('analysis') }}</div>
+    </div>
+    <div class="lal-resp">
+      <div class="log-metrics">
+        <label>{{ $t('metrics') }}</label>
+        <ul>
+          <li>
+            <span v-for="item of logMetricsHeader" :class="item.value" :key="item.value">
+              {{ $t(item.label) }}
+            </span>
+          </li>
+          <li class="no-data" v-show="!rocketLogLAL.logTestResp.metrics.length">{{ $t('noData') }}</li>
+          <li v-for="metric in rocketLogLAL.logTestResp.metrics" :key="metric.name">
+            <span v-for="(item, index) of logMetricsHeader" :class="item.value" :key="item.value + index">
+              <b v-if="item.value === 'tags'">
+                <a v-for="t of metric.tags" :key="t.key">{{ `${t.key}=${t.value};` }} </a>
+              </b>
+              <b v-else-if="item.value === 'timestamp'">{{ metric[item.value] | dateformat }}</b>
+              <b v-else>{{ metric[item.value] }}</b>
+            </span>
+          </li>
+        </ul>
+      </div>
+      <div>
+        <label class="log-detail">{{ $t('log') }}</label>
+        <LogServiceDetailContent :currentLog="rocketLogLAL.logTestResp.log" />
+      </div>
+      <div class="error-tips">
+        <i v-for="err of errorsMessage" :key="err.message">{{ err.message }}</i>
+      </div>
+    </div>
+  </div>
+</template>
+<script lang="ts">
+  import { Component, Vue } from 'vue-property-decorator';
+  import { Action, Getter, Mutation, State } from 'vuex-class';
+  import { State as rocketLogAnaState } from '@/store/modules/debug/log-lal';
+  import { LogTestConstants, TypeList, LogMetricsHeader } from './debug-constant';
+  import { Option } from '@/types/global';
+  import { ConditionTags } from '../common/index';
+  import LogServiceDetailContent from '../log/log-detail-content.vue';
+
+  @Component({ components: { ConditionTags, LogServiceDetailContent } })
+  export default class LogLAL extends Vue {
+    @State('rocketDebugLAL') private rocketLogLAL!: rocketLogAnaState;
+    @State('rocketbot') private rocketbotGlobal: any;
+    @Getter('durationTime') private durationTime: any;
+    @Mutation('SET_SELECTED_SERVICE') private SET_SELECTED_SERVICE: any;
+    @Mutation('SET_SELECTED_ENDPOINT') private SET_SELECTED_ENDPOINT: any;
+    @Mutation('SET_SELECTED_INSTANCE') private SET_SELECTED_INSTANCE: any;
+    @Mutation('SET_LOG_TEST_FIELDS') private SET_LOG_TEST_FIELDS: any;
+    @Mutation('SET_DSL') private SET_DSL: any;
+    @Mutation('SET_LOG_TEST_RESPONSE') private SET_LOG_TEST_RESPONSE: any;
+    @Action('GET_LOG_ANA_ENDPOINTS') private GET_LOG_ANA_ENDPOINTS: any;
+    @Action('GET_LOG_ANA_INSTANCES') private GET_LOG_ANA_INSTANCES: any;
+    @Action('LOG_ANA_QUERY') private LOG_ANA_QUERY: any;
+    @Action('GET_LOG_ANA_SERVICES') private GET_LOG_ANA_SERVICES: any;
+
+    private logTestConstants = LogTestConstants;
+    private time!: Date;
+    private logContent: string = '';
+    private traceID: string = '';
+    private segmentID: string = '';
+    private spanID: string = '';
+    private typeList = TypeList;
+    private logMetricsHeader = LogMetricsHeader;
+    private currentType = {
+      label: 'Text',
+      value: 'text',
+    };
+    private logRespContent: string = '';
+    private showLALResp: boolean = false;
+    private isLoading: boolean = false;
+    private monacoInstance: any;
+    private errorCnt: string = '';
+    private errorsMessage: Array<{ message: string }> = [];
+
+    private created() {
+      this.time = this.rocketbotGlobal.durationRow.start;
+      this.GET_LOG_ANA_SERVICES({ duration: this.durationTime }).then(() => {
+        this.GET_LOG_ANA_INSTANCES({ duration: this.durationTime });
+        this.GET_LOG_ANA_ENDPOINTS({ duration: this.durationTime });
+      });
+    }
+
+    private mounted() {
+      import('monaco-editor/esm/vs/editor/editor.main.js').then((monaco) => {
+        this.monacoInstanceGen(monaco);
+      });
+    }
+
+    private monacoInstanceGen(monaco: any) {
+      this.monacoInstance = monaco.editor.create(this.$refs.dslContent, {
+        value: '',
+        language: 'java',
+      });
+      this.monacoInstance.onDidChangeModelContent((event: any) => {
+        this.SET_DSL(this.monacoInstance.getValue());
+      });
+    }
+
+    private changeLogAnaOptions(type: string, item: Option | any) {
+      if (type === this.logTestConstants.Body) {
+        const contentType = this.typeList.filter(
+          (d: { value: string; label: string }) => d.value === this.currentType.value,
+        )[0];
+        const val = {
+          [contentType.value]: { [contentType.value]: this.logContent },
+        };
+        this.SET_LOG_TEST_FIELDS({ label: type, key: val });
+        return;
+      }
+      if (type === this.logTestConstants.TraceContext) {
+        const val = {
+          traceId: this.traceID,
+          traceSegmentId: this.segmentID,
+          spanId: this.spanID,
+        };
+        this.SET_LOG_TEST_FIELDS({ label: type, key: val });
+        return;
+      }
+      if (type === this.logTestConstants.Type) {
+        this.currentType = { value: item.key, label: item.label };
+        return;
+      }
+      if (type === this.logTestConstants.Service) {
+        this.SET_SELECTED_SERVICE(item);
+        this.GET_LOG_ANA_INSTANCES({ duration: this.durationTime });
+        this.GET_LOG_ANA_ENDPOINTS({ duration: this.durationTime });
+      }
+      if (type === this.logTestConstants.ServiceInstance) {
+        this.SET_SELECTED_INSTANCE(item);
+      }
+      if (type === this.logTestConstants.Endpoint) {
+        this.SET_SELECTED_ENDPOINT(item);
+      }
+      this.SET_LOG_TEST_FIELDS({ label: type, key: item.label });
+    }
+
+    private updateTags(data: { tagsMap: Array<{ key: string; value: string }>; tagsList: string[] }) {
+      this.SET_LOG_TEST_FIELDS({ label: this.logTestConstants.Tags, key: { data: data.tagsMap } });
+    }
+
+    private logAnalysis() {
+      this.SET_LOG_TEST_RESPONSE({ log: {}, metrics: [] });
+      if (this.isLoading) {
+        return;
+      }
+      this.isLoading = true;
+      this.SET_LOG_TEST_FIELDS({ label: this.logTestConstants.Timestamp, key: this.time.getTime() });
+      if (!this.rocketLogLAL.dsl) {
+        this.isLoading = false;
+        this.errorCnt = this.$t('dslEmpty') as string;
+        return;
+      }
+      if (!this.rocketLogLAL.logTestFields.body) {
+        this.isLoading = false;
+        this.errorCnt = this.$t('logContentEmpty') as string;
+        return;
+      }
+      this.LOG_ANA_QUERY().then((errors: Array<{ message: string }>) => {
+        this.showLALResp = true;
+        this.isLoading = false;
+        this.errorsMessage = errors || [];
+      });
+    }
+
+    private unmounted() {
+      this.monacoInstance.dispose();
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .log-lal {
+    overflow: auto;
+    display: flex;
+    flex-direction: row;
+    align-items: flex-start;
+  }
+  .lal-resp {
+    height: 1026px;
+    margin: 10px;
+  }
+  .lal-conditions,
+  .lal-resp {
+    width: 100%;
+    padding: 10px;
+    border: 1px solid #ccc;
+  }
+  .lal-conditions {
+    margin: 10px 10px 20px;
+  }
+  .log-analysis {
+    align-self: center;
+  }
+  input,
+  textarea {
+    border: 1px solid #ccc;
+    width: 100%;
+    display: block;
+    margin-bottom: 6px;
+    outline: none;
+  }
+  .logDataBody {
+    margin-top: 5px;
+    textarea {
+      height: 200px;
+    }
+  }
+  .log-ana-btn {
+    color: #fff;
+    width: 150px;
+    height: 30px;
+    line-height: 30px;
+    text-align: center;
+    border-radius: 4px;
+    cursor: pointer;
+    &.bg-blue {
+      background-color: #448dfe;
+    }
+  }
+  .logRespContent {
+    height: 300px;
+  }
+  ul {
+    max-height: 200px;
+    min-height: 100px;
+    overflow: auto;
+    margin: 5px 0;
+    .tags {
+      width: 40%;
+    }
+  }
+  li {
+    span {
+      width: 20%;
+      line-height: 20px;
+      text-align: center;
+      display: inline-block;
+      border-bottom: 1px solid #ccc;
+    }
+  }
+  .no-data {
+    text-align: center;
+  }
+  #dsl {
+    height: 300px;
+    width: 100%;
+    border: 1px solid #ccc;
+    outline: none;
+  }
+  b {
+    font-weight: normal;
+  }
+  textarea {
+    padding: 5px;
+  }
+  .log-detail {
+    margin-bottom: 10px;
+  }
+  .require,
+  .error-tips {
+    color: red;
+    i {
+      display: block;
+    }
+  }
+  .error-tips {
+    margin-bottom: 20px;
+  }
+  label {
+    font-size: 14px;
+    font-weight: bold;
+  }
+</style>
diff --git a/src/views/components/debug/tool-bar.vue b/src/views/components/debug/tool-bar.vue
new file mode 100644
index 0000000..0417f17
--- /dev/null
+++ b/src/views/components/debug/tool-bar.vue
@@ -0,0 +1,79 @@
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License. -->
+<template>
+  <div class="debug-tool-bar flex-v">
+    <span v-for="(i, index) in tabConstant" :key="index" class="mr-15">
+      <a
+        class="debug-tab mb-10"
+        @click="handleTab(i)"
+        :class="{
+          active: rocketLogLAL.tabType === i,
+          grey: rocketLogLAL.tabType !== i,
+        }"
+      >
+        {{ i }}
+      </a>
+    </span>
+  </div>
+</template>
+
+<script lang="ts">
+  import Vue from 'vue';
+  import { Mutation, State } from 'vuex-class';
+  import Component from 'vue-class-component';
+  import { State as rocketLogAnaState } from '@/store/modules/debug/log-lal';
+
+  @Component({
+    components: {},
+  })
+  export default class DebugToolBar extends Vue {
+    @Mutation('SET_TAB_TYPE') private SET_TAB_TYPE: any;
+    @State('rocketDebugLAL') private rocketLogLAL!: rocketLogAnaState;
+    private tabConstant = ['LAL'];
+    private handleTab(i: string) {
+      this.SET_TAB_TYPE(i);
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .debug-tool-bar {
+    border-bottom: 1px solid #252a2f;
+    background-color: #333840;
+    padding: 10px 15px 0;
+    color: #eee;
+  }
+  .debug-tab {
+    display: inline-block;
+    padding: 4px 13px 4px 15px;
+    border-radius: 4px;
+    position: relative;
+    background-color: rgba(255, 255, 255, 0.07);
+    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.08);
+    will-change: border-color, color;
+    transition: border-color 0.3s, color 0.3s;
+    &.active::before {
+      content: '';
+      position: absolute;
+      display: inline-block;
+      width: 5px;
+      height: 10px;
+      border-radius: 4px;
+      background-color: #448dfe;
+      top: 9px;
+      left: 4px;
+    }
+  }
+</style>
diff --git a/src/views/components/log/log-bar.vue b/src/views/components/log/log-bar.vue
index fe369e5..7f4b58c 100644
--- a/src/views/components/log/log-bar.vue
+++ b/src/views/components/log/log-bar.vue
@@ -75,7 +75,7 @@
         <RkPage :currentSize="pageSize" :currentPage="pageNum" @changePage="handleRefresh" :total="logState.total" />
       </span>
     </div>
-    <div class="flex-h" v-show="showConditionsBox">
+    <div v-show="showConditionsBox">
       <LogConditions />
     </div>
   </div>
@@ -219,7 +219,7 @@
     }
 
     private openConditionsBox() {
-      this.showConditionsBox = !this.showConditionsBox;
+      this.showConditionsBox = true;
     }
   }
 </script>
diff --git a/src/views/components/log/log-browser-detail.vue b/src/views/components/log/log-browser-detail.vue
index 5fc2aef..5c7a94d 100644
--- a/src/views/components/log/log-browser-detail.vue
+++ b/src/views/components/log/log-browser-detail.vue
@@ -40,7 +40,7 @@
 <script lang="ts">
   import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
   import LogTable from './log-table/log-table.vue';
-  import { BrowserLogConstants } from './log-table/log-constant';
+  import { BrowserLogConstants } from './log-constant';
 
   @Component({
     components: { LogTable },
diff --git a/src/views/components/log/log-conditions.vue b/src/views/components/log/log-conditions.vue
index d2b561e..716e972 100644
--- a/src/views/components/log/log-conditions.vue
+++ b/src/views/components/log/log-conditions.vue
@@ -12,7 +12,7 @@
 limitations under the License. -->
 
 <template>
-  <div class="rk-search-conditions flex-v">
+  <div class="rk-search-conditions">
     <div class="flex-h">
       <div class="mr-15" v-show="rocketLog.type.key === cateGoryService">
         <span class="sm b grey mr-10">{{ $t('traceID') }}:</span>
diff --git a/src/views/components/log/log-table/log-constant.ts b/src/views/components/log/log-constant.ts
similarity index 99%
rename from src/views/components/log/log-table/log-constant.ts
rename to src/views/components/log/log-constant.ts
index 079d65a..96030d0 100644
--- a/src/views/components/log/log-table/log-constant.ts
+++ b/src/views/components/log/log-constant.ts
@@ -113,7 +113,6 @@
     label: 'category',
     value: 'category',
   },
-
   {
     label: 'grade',
     value: 'grade',
diff --git a/src/views/components/log/log-detail-content.vue b/src/views/components/log/log-detail-content.vue
new file mode 100644
index 0000000..c56c834
--- /dev/null
+++ b/src/views/components/log/log-detail-content.vue
@@ -0,0 +1,63 @@
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License. -->
+<template>
+  <div class="rk-log-detail">
+    <div class="mb-10 clear rk-flex" v-for="(item, index) in columns" :key="index">
+      <template>
+        <span class="g-sm-4 grey">{{ $t(item.value) }}:</span>
+        <span v-if="item.label === 'timestamp'" class="g-sm-8">{{ currentLog[item.label] | dateformat }}</span>
+        <textarea
+          class="content"
+          readonly="readonly"
+          v-else-if="item.label === 'content'"
+          v-model="currentLog[item.label]"
+        />
+        <span v-else-if="item.label === 'tags'" class="g-sm-8">
+          <div v-for="(d, index) in logTags" :key="index">{{ d }}</div>
+        </span>
+        <span v-else class="g-sm-8">{{ currentLog[item.label] }}</span>
+      </template>
+    </div>
+  </div>
+</template>
+<script lang="ts">
+  import { Component, Prop, Vue } from 'vue-property-decorator';
+  import { ServiceLogDetail } from './log-constant';
+
+  @Component
+  export default class LogServiceDetailContent extends Vue {
+    @Prop() private currentLog: any;
+    private columns = ServiceLogDetail;
+    private logContent: string = '';
+
+    get logTags() {
+      if (!this.currentLog.tags) {
+        return [];
+      }
+      return this.currentLog.tags.map((d: { key: string; value: string }) => {
+        return `${d.key} = ${d.value}`;
+      });
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .content {
+    max-width: 1000px;
+    min-height: 200px;
+    border: none;
+    outline: none;
+    color: #3d444f;
+  }
+</style>
diff --git a/src/views/components/log/log-service-detail.vue b/src/views/components/log/log-service-detail.vue
index 0c2e82e..972d44f 100644
--- a/src/views/components/log/log-service-detail.vue
+++ b/src/views/components/log/log-service-detail.vue
@@ -23,19 +23,7 @@
       <div class="log-tips" v-if="!data.length">{{ $t('noData') }}</div>
     </LogTable>
     <rk-sidebox :width="'800px'" :show.sync="showDetail" :title="$t('logDetail')">
-      <div class="rk-log-detail">
-        <div class="mb-10 clear rk-flex" v-for="(item, index) in columns" :key="index">
-          <template>
-            <span class="g-sm-4 grey">{{ $t(item.value) }}:</span>
-            <span v-if="item.label === 'timestamp'" class="g-sm-8">{{ currentLog[item.label] | dateformat }}</span>
-            <textarea class="content" readonly="readonly" v-else-if="item.label === 'content'" v-model="logContent" />
-            <span v-else-if="item.label === 'tags'" class="g-sm-8">
-              <div v-for="(d, index) in logTags" :key="index">{{ d }}</div>
-            </span>
-            <span v-else class="g-sm-8">{{ currentLog[item.label] }}</span>
-          </template>
-        </div>
-      </div>
+      <LogServiceDetailContent :currentLog="currentLog" />
     </rk-sidebox>
   </div>
 </template>
@@ -43,11 +31,12 @@
 <script lang="ts">
   import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
   import LogTable from './log-table/log-table.vue';
-  import { ServiceLogDetail } from './log-table/log-constant';
+  import LogServiceDetailContent from './log-detail-content.vue';
+  import { ServiceLogDetail } from './log-constant';
   import { formatJson } from '@/utils/formatJson';
 
   @Component({
-    components: { LogTable },
+    components: { LogTable, LogServiceDetailContent },
   })
   export default class LogServiceDetail extends Vue {
     @Prop() private data: any;
@@ -59,13 +48,9 @@
     private list = [];
     private currentLog: any = {};
     private logContent: string = '';
-    private logTags: string = '';
 
     private handleSelectLog(data: any[]) {
       this.currentLog = data;
-      this.logTags = this.currentLog.tags.map((d: { key: string; value: string }) => {
-        return `${d.key} = ${d.value}`;
-      });
       if (this.currentLog.contentType === 'JSON') {
         this.logContent = formatJson(JSON.parse(this.currentLog.content));
       } else {
@@ -109,11 +94,4 @@
   .g-sm-4.grey {
     flex-shrink: 0;
   }
-  .content {
-    width: 500px;
-    height: 500px;
-    border: none;
-    outline: none;
-    color: #3d444f;
-  }
 </style>
diff --git a/src/views/components/log/log-table/log-browser-item.vue b/src/views/components/log/log-table/log-browser-item.vue
index 8394d03..b9349ed 100644
--- a/src/views/components/log/log-table/log-browser-item.vue
+++ b/src/views/components/log/log-table/log-browser-item.vue
@@ -32,7 +32,7 @@
 </template>
 <script lang="ts">
   import { Component, Prop, Vue } from 'vue-property-decorator';
-  import { BrowserLogConstants } from './log-constant';
+  import { BrowserLogConstants } from '../log-constant';
 
   @Component
   export default class ServiceItem extends Vue {
diff --git a/src/views/components/log/log-table/log-service-item.vue b/src/views/components/log/log-table/log-service-item.vue
index a4601fa..2b051f4 100644
--- a/src/views/components/log/log-table/log-service-item.vue
+++ b/src/views/components/log/log-table/log-service-item.vue
@@ -34,7 +34,7 @@
 </template>
 <script lang="ts">
   import { Component, Prop, Vue } from 'vue-property-decorator';
-  import { ServiceLogConstants } from './log-constant';
+  import { ServiceLogConstants } from '../log-constant';
 
   @Component
   export default class ServiceItem extends Vue {
diff --git a/src/views/components/log/log-table/log-table.vue b/src/views/components/log/log-table/log-table.vue
index 887d607..62d16f2 100644
--- a/src/views/components/log/log-table/log-table.vue
+++ b/src/views/components/log/log-table/log-table.vue
@@ -38,7 +38,7 @@
   </div>
 </template>
 <script lang="js">
-  import { ServiceLogConstants, BrowserLogConstants } from './log-constant';
+  import { ServiceLogConstants, BrowserLogConstants } from '../log-constant';
   import BrowserItem from './log-browser-item';
   import ServiceItem from './log-service-item';
 
diff --git a/src/views/containers/debug.vue b/src/views/containers/debug.vue
new file mode 100644
index 0000000..55bde5e
--- /dev/null
+++ b/src/views/containers/debug.vue
@@ -0,0 +1,40 @@
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License. -->
+<template>
+  <div class="rk-debug flex-v">
+    <DebugToolBar />
+    <LogLAL />
+  </div>
+</template>
+
+<script lang="ts">
+  import Vue from 'vue';
+  import Component from 'vue-class-component';
+  import DebugToolBar from '../components/debug/tool-bar.vue';
+  import LogLAL from '../components/debug/log-lal.vue';
+
+  @Component({
+    components: { DebugToolBar, LogLAL },
+  })
+  export default class Debug extends Vue {}
+</script>
+
+<style lang="scss" scoped>
+  .rk-debug {
+    flex-grow: 1;
+    height: 100%;
+    min-height: 0;
+  }
+</style>
diff --git a/vue.config.js b/vue.config.js
index ee2b28d..63e5c05 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
+const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
 module.exports = {
   productionSourceMap: process.env.NODE_ENV !== 'production',
   devServer: {
@@ -35,8 +35,9 @@
         symbolId: '[name]',
       });
   },
-  configureWebpack: (config) => {
-    config.optimization = {
+  configureWebpack: {
+    plugins: [new MonacoWebpackPlugin()],
+    optimization: {
       splitChunks: {
         chunks: 'all',
         cacheGroups: {
@@ -45,8 +46,13 @@
             test: /[\\/]node_modules[\\/]echarts[\\/]/,
             priority: 2,
           },
+          monacoEditor: {
+            name: 'monaco-editor',
+            test: /[\\/]node_modules[\\/]monaco-editor[\\/]/,
+            priority: 1,
+          },
         },
       },
-    };
+    },
   },
 };