Merge pull request #4905 from WeDataSphere/master-1.1.15-merge

[Feature][Extensions] Add Monitor Server
diff --git a/linkis-dist/bin/install.sh b/linkis-dist/bin/install.sh
index d7e5212..d6e5022 100644
--- a/linkis-dist/bin/install.sh
+++ b/linkis-dist/bin/install.sh
@@ -127,6 +127,7 @@
 if [ $DEBUG_MODE != "true" ];then
   sed -i ${txt}  "s#BML-AUTH#$RANDOM_BML_TOKEN#g" $LINKIS_HOME/conf/linkis-cli/linkis-cli.properties
   sed -i ${txt}  "s#BML-AUTH#$RANDOM_BML_TOKEN#g" $common_conf
+  sed -i ${txt}  "s#BML-AUTH#$RANDOM_BML_TOKEN#g" $LINKIS_HOME/admin/configuration_helper.sh
   sed -i ${txt}  "s#LINKIS_CLI_TEST#$RANDOM_LINKIS_CLI_TEST_TOKEN#g" $common_conf
   sed -i ${txt}  "s#WS-AUTH#$RANDOM_WS_TOKEN#g" $common_conf
   sed -i ${txt}  "s#DSM-AUTH#$RANDOM_DSM_TOKEN#g" $common_conf
@@ -532,6 +533,7 @@
 if [ "$RESULT_SET_ROOT_PATH" != "" ]
 then
   sed -i ${txt}  "s#wds.linkis.resultSet.store.path.*#wds.linkis.resultSet.store.path=$RESULT_SET_ROOT_PATH#g" $entrance_conf
+  sed -i ${txt}  "s#resultSetRootDir=.*#resultSetRootDir=$RESULT_SET_ROOT_PATH#g" $LINKIS_HOME/admin/linkis_task_res_log_clear.sh
 fi
 
 publicservice_conf=$LINKIS_HOME/conf/linkis-ps-publicservice.properties
diff --git a/linkis-dist/package/admin/clear_ec_record.sh b/linkis-dist/package/admin/clear_ec_record.sh
new file mode 100644
index 0000000..5cd1525
--- /dev/null
+++ b/linkis-dist/package/admin/clear_ec_record.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# 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.
+#
+
+#
+# description:  clear linkis_ps_job_history_group_history  10 days ago record
+#
+if [ -f ${LINKIS_CONF_DIR}/db.sh ]
+then
+   export LINKIS_DB_CONFIG_PATH=${LINKIS_CONF_DIR}/db.sh
+else
+   if [ -f ${LINKIS_HOME}/conf/db.sh ]
+   then
+      export LINKIS_DB_CONFIG_PATH=${LINKIS_HOME}/conf/db.sh
+   else
+      echo "can not find db.sh"
+      exit
+   fi
+fi
+source ${LINKIS_DB_CONFIG_PATH}
+
+delete_day=`date -d "-10 days"  "+%Y-%m-%d"`
+delte_time="$delete_day 00:00:00"
+echo "start to delete linkis_cg_ec_resource_info_record before $delte_time"
+parm="release_time <=\"$delte_time\" "
+
+count=`mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER  -p$MYSQL_PASSWORD  $MYSQL_DB -ss -e "SELECT count(1) FROM linkis_cg_ec_resource_info_record where $parm limit 1"`
+maxid=`mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER  -p$MYSQL_PASSWORD  $MYSQL_DB -ss -e "SELECT MAX(id) FROM linkis_cg_ec_resource_info_record where $parm limit 1"`
+echo "will delete count:$count"
+echo "maxid:$maxid"
+
+while [ $count -gt 1 ];do
+    mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER  -p$MYSQL_PASSWORD  $MYSQL_DB -ss -e "DELETE FROM linkis_cg_ec_resource_info_record  where id <= $maxid limit 5000;"
+    count=`mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER  -p$MYSQL_PASSWORD  $MYSQL_DB -ss -e "SELECT count(1) FROM linkis_cg_ec_resource_info_record where $parm limit 1"`
+    echo "count change : $count"
+    sleep 1s
+done
+
+echo "clear_ec_record.sh  over"
\ No newline at end of file
diff --git a/linkis-dist/package/admin/clear_history_task.sh b/linkis-dist/package/admin/clear_history_task.sh
new file mode 100644
index 0000000..75c49cb
--- /dev/null
+++ b/linkis-dist/package/admin/clear_history_task.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+#
+# 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.
+#
+
+#
+# description:  clear linkis_ps_job_history_group_history  3 month record
+#
+if [ -f ${LINKIS_CONF_DIR}/db.sh ]
+then
+   export LINKIS_DB_CONFIG_PATH=${LINKIS_CONF_DIR}/db.sh
+else
+   if [ -f ${LINKIS_HOME}/conf/db.sh ]
+   then
+      export LINKIS_DB_CONFIG_PATH=${LINKIS_HOME}/conf/db.sh
+   else
+      echo "can not find db.sh"
+      exit
+   fi
+fi
+source ${LINKIS_DB_CONFIG_PATH}
+
+delete_day=`date -d "-90 days"  "+%Y-%m-%d"`
+delte_time="$delete_day 00:00:00"
+echo "start to delete linkis_ps_job_history_group_history before $delte_time"
+parm="created_time <=\"$delte_time\" "
+
+count=`mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER  -p$MYSQL_PASSWORD  $MYSQL_DB -ss -e "SELECT count(1) FROM linkis_ps_job_history_group_history where $parm limit 1 "`
+maxid=`mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER  -p$MYSQL_PASSWORD  $MYSQL_DB -ss -e "SELECT MAX(id) FROM linkis_ps_job_history_group_history where $parm limit 1 "`
+echo "will delete count:$count"
+echo "maxid:$maxid"
+
+while [ $count -gt 1 ];do
+   mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER  -p$MYSQL_PASSWORD  $MYSQL_DB -ss -e "DELETE FROM linkis_ps_job_history_group_history where id <= $maxid limit 5000;"
+   count=`mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER  -p$MYSQL_PASSWORD  $MYSQL_DB -ss -e "SELECT count(1) FROM linkis_ps_job_history_group_history where $parm limit 1 "`
+   echo "count change : $count"
+   sleep 1s
+done
\ No newline at end of file
diff --git a/linkis-dist/package/admin/configuration_helper.sh b/linkis-dist/package/admin/configuration_helper.sh
new file mode 100644
index 0000000..8c918df
--- /dev/null
+++ b/linkis-dist/package/admin/configuration_helper.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+#
+# 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.
+#
+
+
+cd `dirname $0`
+cd ..
+INSTALL_HOME=`pwd`
+
+
+function print_usage(){
+  echo "Usage: configuration_helper.sh [add | get | delete] [engineType] [version] [creator] [configKey] [configValue option]"
+  echo "get eq: sh  configuration_helper.sh get spark 2.4.3 test wds.linkis.rm.instance hadoop"
+  echo "delete eq: sh  configuration_helper.sh delete spark 2.4.3 test wds.linkis.rm.instance hadoop"
+  echo "add eq: sh  configuration_helper.sh add spark 2.4.3 test wds.linkis.rm.instance hadoop 6"
+  echo "add eq: sh  configuration_helper.sh add spark 2.4.3 test wds.linkis.rm.instance hadoop 6 force"
+  echo "add tips: add with force will ignore check error"
+  echo "Most commands print help when invoked w/o parameters."
+}
+
+if [ $# -lt 6 ]; then
+  print_usage
+  exit 2
+fi
+
+# set LINKIS_HOME
+if [ "$LINKIS_HOME" = "" ]; then
+  export LINKIS_HOME=$INSTALL_HOME
+fi
+
+# set LINKIS_CONF_DIR
+if [ "$LINKIS_CONF_DIR" = "" ]; then
+  export LINKIS_CONF_DIR=$LINKIS_HOME/conf
+fi
+linkisMainConf=$LINKIS_CONF_DIR/linkis.properties
+gatewayUrl=$(grep wds.linkis.gateway.url $linkisMainConf | cut -d"=" -f2)
+echo "gatewayUrl: $gatewayUrl"
+engineType=$2
+version=$3
+creator=$4
+configKey=$5
+user=$6
+configValue=$7
+COMMAND=$1
+if [ "$8" = "force" ]; then
+ force=true
+fi
+
+get()
+{
+  requestUrl="$gatewayUrl/api/rest_j/v1/configuration/keyvalue?creator=$creator&engineType=$engineType&version=$version&configKey=$configKey"
+  curl --location --request GET $requestUrl -H "Token-Code:BML-AUTH" -H "Token-User:$user"
+}
+
+delete()
+{
+  requestUrl="$gatewayUrl/api/rest_j/v1/configuration/keyvalue"
+  requestBody="{\"engineType\":\"$engineType\",\"version\":\"$version\",\"creator\":\"$creator\",\"configKey\":\"$configKey\"}"
+  curl -i -X DELETE $requestUrl  -H "Accept: application/json"  -H "Content-Type: application/json" -H "Token-Code:BML-AUTH" -H "Token-User:$user" -d "$requestBody"
+}
+
+add()
+{
+  requestUrl="$gatewayUrl/api/rest_j/v1/configuration/keyvalue"
+  requestBody="{\"engineType\":\"$engineType\",\"version\":\"$version\",\"creator\":\"$creator\",\"configKey\":\"$configKey\",\"configValue\":\"$configValue\",\"force\":\"$force\",\"user\":\"$user\"}"
+  curl -i -X POST $requestUrl  -H "Accept: application/json"  -H "Content-Type: application/json" -H "Token-Code:BML-AUTH" -H "Token-User:hadoop" -d "$requestBody"
+}
+
+case $COMMAND in
+  add|get|delete)
+    $COMMAND
+    ;;
+  *)
+    print_usage
+    exit 2
+    ;;
+esac
diff --git a/linkis-dist/package/admin/linkis_task_res_log_clear.sh b/linkis-dist/package/admin/linkis_task_res_log_clear.sh
new file mode 100644
index 0000000..4272633
--- /dev/null
+++ b/linkis-dist/package/admin/linkis_task_res_log_clear.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+#
+# 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.
+#
+
+#!/bin/bash
+expiredDays=365
+resultSetRootDir=/tmp/linkis
+logRootDir=/tmp/linkis
+userResultSetDir=$(hdfs dfs -ls $resultSetRootDir | awk '{print $8}')
+realLogRootDir=$logRootDir/log
+echo userResultSetDirs: $userResultSetDir
+echo realLogRootDir: $realLogRootDir
+
+if [ -z $LINKIS_LOG_DIR ];then
+ expiredFileRecordDir=${LINKIS_HOME}/expiredFileRecord
+else
+ expiredFileRecordDir=$LINKIS_LOG_DIR/expiredFileRecord
+fi
+
+function createExpiredFileRecoredDir(){
+ if [ ! -d $expiredFileRecordDir ];then
+  mkdir -p $expiredFileRecordDir
+ fi
+}
+
+createExpiredFileRecoredDir
+expireDate=$(date -d -${expiredDays}day +%Y-%m-%d)
+expireResultSetFile=$expiredFileRecordDir/linkis_expire_resultset_dir_${expireDate}.txt
+expireLogFile=$expiredFileRecordDir/linkis_expire_log_dir_${expireDate}.txt
+
+hdfs dfs -ls $realLogRootDir | awk '$8 ~ /.*linkis\/log\/[0-9|\-|\_]*/ {cmd = "date -d -12month +%Y-%m-%d";cmd | getline oneMonthAgo;if($6 < oneMonthAgo) print $8}' >> $expireLogFile
+
+for i in $userResultSetDir
+do
+  hdfs dfs -ls $i/linkis | awk '$8 ~ /.*linkis\/[0-9\-]{10}/ {cmd = "date -d -12month +%Y-%m-%d";cmd | getline oneMonthAgo;if($6 < oneMonthAgo) print $8}' >> $expireResultSetFile
+done
+
+cat $expireLogFile | xargs -n 1000 hdfs dfs -rm -r -f
+
+cat $expireResultSetFile | xargs -n 1000 hdfs dfs -rm -r -f
+
+
diff --git a/linkis-dist/package/conf/linkis-et-monitor-file.properties b/linkis-dist/package/conf/linkis-et-monitor-file.properties
new file mode 100644
index 0000000..22a4584
--- /dev/null
+++ b/linkis-dist/package/conf/linkis-et-monitor-file.properties
@@ -0,0 +1,143 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+jobhistory.errorcode.01002={"alert_title":"Linkis Service load is too high, please contact Linkis owner","alert_info":"Linkis Linkis Service load is too high, please contact Linkis owner","alert_way":"1,2,3","alert_reciver":"hadoop","alert_level":"2","alert_obj":"linkis-alert-obj","can_recover":"0"}
+jobhistory.errorcode.01003={"alert_title":"Linkis Service load is too high, please contact Linkis owner","alert_info":"Linkis Service load is too high, please contact Linkis owner","alert_way":"1,2,3","alert_reciver":"hadoop","alert_level":"2","alert_obj":"linkis-alert-obj","can_recover":"0"}
+jobhistory.errorcode.01101={"alert_title":"Linkis Service load is too high, please contact Linkis owner","alert_info":" Service load is too high, please contact Linkis owner","alert_way":"1,2,3","alert_reciver":"hadoop","alert_level":"2","alert_obj":"linkis-alert-obj","can_recover":"0"}
+jobhistory.errorcode.01102={"alert_title":"Linkis ECM memory  Service load is too high, please contact Linkis owner","alert_info":" Service load is too high, please contact Linkis owner","alert_way":"1,2,3","alert_reciver":"hadoop","alert_level":"2","alert_obj":"linkis-alert-obj","can_recover":"0"}
+jobhistory.errorcode.01103={"alert_title":"Linkis ECM CPU Service load is too high, please contact Linkis owner","alert_info":" Service load is too high, please contact Linkis owner","alert_way":"1,2,3","alert_reciver":"hadoop","alert_level":"2","alert_obj":"linkis-alert-obj","can_recover":"0"}
+jobhistory.errorcode.01104={"alert_title":"Linkis ECM instances  Service load is too high, please contact Linkis owner","alert_info":" Service load is too high, please contact Linkis owner","alert_way":"1,2,3","alert_reciver":"hadoop","alert_level":"2","alert_obj":"linkis-alert-obj","can_recover":"0"}
+jobhistory.errorcode.01105={"alert_title":"Linkis Memory  Service load is too high, please contact Linkis owner","alert_info":"The machine has insufficient memory. Please contact the administrator to expand the memory.","alert_way":"1,2,3","alert_reciver":"hadoop","alert_level":"2","alert_obj":"linkis-alert-obj","can_recover":"0"}
+jobhistory.unfinished.time.exceed.sec.43200={"alert_title":"Linkis task execute timeout","alert_info":"Linkis task execute timeout 12h","alert_way":"1,2,3","alert_reciver":"","alert_level":"4","alert_obj":"Linkis","can_recover":"0"}
+
+ecm.resource.monitor.im.12003=\
+  {"alert_title":"Linkis Monitor Resource Alert",\
+  "alert_info":" $instance ECM Memory/cpu insufficient resource < 0.2 please contact Linkis owner: $name",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"hadoop",\
+  "alert_level":"3",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+ecm.resource.monitor.im.12004=\
+  {"alert_title":"Linkis Monitor Resource Alert",\
+  "alert_info":" $instance ECM Memory/cpu insufficient resource < 0.2 please contact Linkis owner: $name",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"hadoop",\
+  "alert_level":"2",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+ecm.resource.monitor.im.12005=\
+  {"alert_title":"Linkis entrance user running task monitor",\
+  "alert_info":"User $username runninng task at linkis ( $url ) >  $runningtask ,please attention",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"hadoop",\
+  "alert_level":"3",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+ecm.resource.monitor.im.12006=\
+  {"alert_title":"Linkis entrance user queued task monitor",\
+  "alert_info":"User $username queued task at linkis ( $url ) >   $queuedtask ,please attention",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"hadoop",\
+  "alert_level":"3",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+ecm.resource.monitor.im.12007=\
+  {"alert_title":"Linkis entrance user total task monitor",\
+  "alert_info":"User $username queued task at linkis ( $url ) >   $tasktotal ,please attention",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"hadoop",\
+  "alert_level":"3",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+ecm.resource.monitor.im.12008=\
+  {"alert_title":"Linkis entrance all running task monitor",\
+  "alert_info":"linkis ( $url ) running task >  $taskminor,please attention",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"hadoop",\
+  "alert_level":"3",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+ecm.resource.monitor.im.12009=\
+  {"alert_title":"Linkis entrance all running task monitor",\
+  "alert_info":"linkis ( $url ) all task >  $taskmajor,please attention",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"hadoop",\
+  "alert_level":"2",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+
+
+
+user.mode.monitor.im.12011=\
+  {"alert_title":"User mode execution timeout alarm",\
+ "alert_info":"User mode execution timeout alarm Linkis url: $url engineType:$engineType Task ID: $jobId please attention  $name",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"alexyang,hadoop",\
+  "alert_level":"2",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+user.mode.monitor.im.12012=\
+  {"alert_title":"User mode execution failure alarm",\
+  "alert_info":"User mode execution failure alarm Linkis url: $url Engine: $engineType TaskID: $jobId ER=rrorCode?$errorCode  errorMsg: $errorMsg please attention $name",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"alexyang,hadoop",\
+  "alert_level":"2",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+
+user.mode.monitor.im.12017=\
+  {"alert_title":"@alert_title",\
+  "alert_info":"task execute failed, reason $msg",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"@alert_reciver",\
+  "alert_level":"@alert_level",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+user.mode.monitor.im.12018=\
+  {"alert_title":"@alert_title",\
+  "alert_info":"task execute time out $timeout",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"@alert_reciver",\
+  "alert_level":"@alert_level",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+
+thread.monitor.timeout.im.12014=\
+  {"alert_title":" Linkis Shell Timeout Alert ",\
+  "alert_info":"Monitor Shell execute time out $shellName",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"hadoop",\
+  "alert_level":"3",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+jobhistory.result.monitor.im.12015=\
+  {"alert_title":"Linkis \u4EFB\u52A1\u72B6\u6001\u901A\u77E5",\
+  "alert_info":"Task ID:$id  final status: $status",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"",\
+  "alert_level":"4",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+jobhistory.result.monitor.im.12016=\
+  {"alert_title":"@alert_title",\
+  "alert_info":"[Alarm time]$date\n[Subsystem]$sysid\n[Alarm IP]$ip\n[Alarm object]$object\n[Alarm information]$detail",\
+  "alert_way":"1,2,3",\
+  "alert_reciver":"@alert_reciver",\
+  "alert_level":"@alert_level",\
+  "alert_obj":"linkis_alert",\
+  "can_recover":"0"}
+
+
diff --git a/linkis-dist/package/conf/linkis-et-monitor.properties b/linkis-dist/package/conf/linkis-et-monitor.properties
new file mode 100644
index 0000000..6d80294
--- /dev/null
+++ b/linkis-dist/package/conf/linkis-et-monitor.properties
@@ -0,0 +1,84 @@
+#
+# 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.
+#
+
+wds.linkis.server.mybatis.typeAliasesPackage=org.apache.linkis.monitor.scan.app.instance.entity,org.apache.linkis.monitor.scan.app.jobhistory.entity,org.apache.linkis.bml.cleaner.dao
+wds.linkis.server.mybatis.BasePackage=org.apache.linkis.monitor.scan.app.instance.dao,org.apache.linkis.monitor.scan.app.jobhistory.dao,org.apache.linkis.bml.cleaner.dao
+
+
+# alert server url
+linkis.alert.url=http://127.0.0.1/
+
+# alert receiver
+linkis.alert.receiver.default=hadoop
+
+# monitor ecm  resource
+linkis.monitor.ecm.resource.cron=0 0/10 * * * ? 
+
+# Resource remaining ratio
+linkis.monitor.ecmResourceTask.major=0.005
+linkis.monitor.ecmResourceTask.minor=0.01
+
+# entrance task metrics cron
+linkis.monitor.entrance.task.cron=0 0/10 * * * ?
+
+# Timeout task  cron
+linkis.monitor.jobHistory.timeout.cron=0 0/20 * * * ? 
+
+# time out interval 24h
+linkis.monitor.scanner.timeout.interval.seconds=86400
+
+
+# Finished  task  cron
+linkis.monitor.jobHistory.finished.cron=0 0/20 * * * ?
+
+# linkis user mode cron
+linkis.monitor.user.mode.cron=0 0/20 * * * ?
+
+# user mode for engine
+linkis.monitor.user.enginelist=[{"engineType":"hive-2.3.3","code":"show tables","runType":"hql","executeUser":"hadoop"},\
+                                {"engineType":"spark-2.4.3","code":"show tables","runType":"sql","executeUser":"hadoop"},\
+                                {"engineType":"shell-1","code":"pwd","runType":"sh","executeUser":"hadoop"}]
+
+linkis.monitor.user.mode.timeOut=300
+
+
+# bml clear cron
+linkis.monitor.bml.clear.history.version.cron=0 0 12 * * ?
+# bml max version
+linkis.bml.cleaner.version.max.num=50
+# keep verssion
+linkis.bml.cleaner.version.keep.num=20
+# clear max interval
+linkis.bml.cleaner.previous.interval.days=30
+# once scan limit
+linkis.bml.cleaner.once.limit.num=100
+
+# clear db ec record cron
+linkis.monitor.clear.ecRecord.cron=0 10 12 * * ?
+
+# clear task log  cron
+linkis.monitor.clear.taskLog.cron=0 10 12 * * ?
+
+# clear db task history   cron
+linkis.monitor.clear.history.task.cron=0 0 13 * * ?
+
+# poll size
+linkis.monitor.scheduled.pool.cores.num=20
+
+# shell time out conf minute
+linkis.monitor.shell.time.out.minute=60
+
+##Spring
+spring.server.port=9119
diff --git a/linkis-dist/package/sbin/ext/linkis-et-monitor b/linkis-dist/package/sbin/ext/linkis-et-monitor
new file mode 100644
index 0000000..e0c78c4
--- /dev/null
+++ b/linkis-dist/package/sbin/ext/linkis-et-monitor
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+#
+# 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.
+#
+# description:  monitor start cmd
+#
+
+
+source $LINKIS_CONF_DIR/linkis-env.sh
+export SERVER_SUFFIX="linkis-extensions/linkis-et-monitor"
+
+export SERVER_CLASS=org.apache.linkis.monitor.LinksMonitorApplication
+
+if test -z "$MONITOR_HEAP_SIZE"
+  then
+  if test -z "$SERVER_HEAP_SIZE"
+  then
+    export SERVER_HEAP_SIZE="512M"
+  fi
+else
+  export SERVER_HEAP_SIZE=$MONITOR_HEAP_SIZE
+fi
+
+#export DEBUG_PORT=
+
+export COMMON_START_BIN=$LINKIS_HOME/sbin/ext/linkis-common-start
+if [[ ! -f "${COMMON_START_BIN}" ]]; then
+    echo "The $COMMON_START_BIN  does not exist!"
+    exit 1
+else
+    sh $COMMON_START_BIN
+fi
\ No newline at end of file
diff --git a/linkis-dist/src/main/assembly/distribution.xml b/linkis-dist/src/main/assembly/distribution.xml
index 57fd07d..a506c99 100644
--- a/linkis-dist/src/main/assembly/distribution.xml
+++ b/linkis-dist/src/main/assembly/distribution.xml
@@ -261,6 +261,21 @@
             </includes>
         </fileSet>
 
+        <!--lib-->
+        <!--linkis-public-enhancements-->
+        <!--bml-->
+        <fileSet>
+            <directory>
+                ../linkis-extensions/linkis-et-monitor/target/out/lib
+            </directory>
+            <outputDirectory>
+                linkis-package/lib/linkis-extensions/linkis-et-monitor
+            </outputDirectory>
+            <includes>
+                <include>**/*</include>
+            </includes>
+        </fileSet>
+
     </fileSets>
 
     <dependencySets>
diff --git a/linkis-extensions/linkis-et-monitor/pom.xml b/linkis-extensions/linkis-et-monitor/pom.xml
new file mode 100644
index 0000000..0e5f44c
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/pom.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2019 WeBank
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~  you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.linkis</groupId>
+    <artifactId>linkis</artifactId>
+    <version>${revision}</version>
+    <relativePath>../../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>linkis-et-monitor</artifactId>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.linkis</groupId>
+      <artifactId>linkis-httpclient</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.linkis</groupId>
+      <artifactId>linkis-mybatis</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.linkis</groupId>
+      <artifactId>linkis-storage</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.linkis</groupId>
+      <artifactId>linkis-rpc</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.linkis</groupId>
+      <artifactId>linkis-gateway-httpclient-support</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.linkis</groupId>
+      <artifactId>linkis-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.linkis</groupId>
+      <artifactId>linkis-computation-client</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>net.alchim31.maven</groupId>
+        <artifactId>scala-maven-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <inherited>false</inherited>
+        <configuration>
+          <skipAssembly>false</skipAssembly>
+          <finalName>out</finalName>
+          <appendAssemblyId>false</appendAssemblyId>
+          <attach>false</attach>
+          <descriptors>
+            <descriptor>src/main/assembly/distribution.xml</descriptor>
+          </descriptors>
+        </configuration>
+        <executions>
+          <execution>
+            <id>make-assembly</id>
+            <goals>
+              <goal>single</goal>
+            </goals>
+            <phase>package</phase>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/linkis-extensions/linkis-et-monitor/src/main/assembly/distribution.xml b/linkis-extensions/linkis-et-monitor/src/main/assembly/distribution.xml
new file mode 100644
index 0000000..e606ed7
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/assembly/distribution.xml
@@ -0,0 +1,296 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.1 https://maven.apache.org/xsd/assembly-2.1.1.xsd">
+    <id>linkis-et-monitor</id>
+    <formats>
+        <format>dir</format>
+    </formats>
+    <includeBaseDirectory>false</includeBaseDirectory>
+    <baseDirectory>linkis-et-monitor</baseDirectory>
+
+    <dependencySets>
+        <dependencySet>
+            <!-- Enable access to all projects in the current multimodule build! <useAllReactorProjects>true</useAllReactorProjects> -->
+            <!-- Now, select which projects to include in this module-set. -->
+            <outputDirectory>lib</outputDirectory>
+            <useProjectArtifact>true</useProjectArtifact>
+            <useTransitiveDependencies>true</useTransitiveDependencies>
+            <unpack>false</unpack>
+            <useStrictFiltering>false</useStrictFiltering>
+            <useTransitiveFiltering>true</useTransitiveFiltering>
+
+            <excludes>
+                <exclude>antlr:antlr:jar</exclude>
+                <exclude>aopalliance:aopalliance:jar</exclude>
+                <exclude>asm:asm:jar</exclude>
+                <exclude>cglib:cglib:jar</exclude>
+                <exclude>com.amazonaws:aws-java-sdk-autoscaling:jar</exclude>
+                <exclude>com.amazonaws:aws-java-sdk-core:jar</exclude>
+                <exclude>com.amazonaws:aws-java-sdk-ec2:jar</exclude>
+                <exclude>com.amazonaws:aws-java-sdk-route53:jar</exclude>
+                <exclude>com.amazonaws:aws-java-sdk-sts:jar</exclude>
+                <exclude>com.amazonaws:jmespath-java:jar</exclude>
+                <exclude>com.fasterxml.jackson.core:jackson-annotations:jar</exclude>
+                <exclude>com.fasterxml.jackson.core:jackson-core:jar</exclude>
+                <exclude>com.fasterxml.jackson.core:jackson-databind:jar</exclude>
+                <exclude>com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:jar</exclude>
+                <exclude>com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar</exclude>
+                <exclude>com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar</exclude>
+                <exclude>com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar</exclude>
+                <exclude>com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar</exclude>
+                <exclude>com.fasterxml.jackson.module:jackson-module-jaxb-annotations:jar</exclude>
+                <exclude>com.fasterxml.jackson.module:jackson-module-parameter-names:jar</exclude>
+                <exclude>com.fasterxml.jackson.module:jackson-module-paranamer:jar</exclude>
+                <exclude>com.fasterxml.jackson.module:jackson-module-scala_2.11:jar</exclude>
+                <exclude>com.github.andrewoma.dexx:dexx-collections:jar</exclude>
+                <exclude>com.github.vlsi.compactmap:compactmap:jar</exclude>
+                <exclude>com.google.code.findbugs:annotations:jar</exclude>
+                <exclude>com.google.code.findbugs:jsr305:jar</exclude>
+                <exclude>com.google.code.gson:gson:jar</exclude>
+                <exclude>com.google.guava:guava:jar</exclude>
+                <exclude>com.google.inject:guice:jar</exclude>
+                <exclude>com.google.protobuf:protobuf-java:jar</exclude>
+                <exclude>com.netflix.archaius:archaius-core:jar</exclude>
+                <exclude>com.netflix.eureka:eureka-client:jar</exclude>
+                <exclude>com.netflix.eureka:eureka-core:jar</exclude>
+                <exclude>com.netflix.hystrix:hystrix-core:jar</exclude>
+                <exclude>com.netflix.netflix-commons:netflix-commons-util:jar</exclude>
+                <exclude>com.netflix.netflix-commons:netflix-eventbus:jar</exclude>
+                <exclude>com.netflix.netflix-commons:netflix-infix:jar</exclude>
+                <exclude>com.netflix.netflix-commons:netflix-statistics:jar</exclude>
+                <exclude>com.netflix.ribbon:ribbon:jar</exclude>
+                <exclude>com.netflix.ribbon:ribbon-core:jar</exclude>
+                <exclude>com.netflix.ribbon:ribbon-eureka:jar</exclude>
+                <exclude>com.netflix.ribbon:ribbon-httpclient:jar</exclude>
+                <exclude>com.netflix.ribbon:ribbon-loadbalancer:jar</exclude>
+                <exclude>com.netflix.ribbon:ribbon-transport:jar</exclude>
+                <exclude>com.netflix.servo:servo-core:jar</exclude>
+                <exclude>com.ning:async-http-client:jar</exclude>
+                <exclude>com.sun.jersey.contribs:jersey-apache-client4:jar</exclude>
+                <exclude>com.sun.jersey:jersey-client:jar</exclude>
+                <exclude>com.sun.jersey:jersey-core:jar</exclude>
+                <exclude>com.sun.jersey:jersey-json:jar</exclude>
+                <exclude>com.sun.jersey:jersey-server:jar</exclude>
+                <exclude>com.sun.jersey:jersey-servlet:jar</exclude>
+                <exclude>com.sun.xml.bind:jaxb-impl:jar</exclude>
+                <exclude>com.thoughtworks.paranamer:paranamer:jar</exclude>
+                <exclude>com.thoughtworks.xstream:xstream:jar</exclude>
+                <exclude>org.apache.linkis:linkis-common:jar</exclude>
+                <exclude>org.apache.linkis:linkis-module:jar</exclude>
+                <exclude>commons-beanutils:commons-beanutils:jar</exclude>
+                <exclude>commons-beanutils:commons-beanutils-core:jar</exclude>
+                <exclude>commons-cli:commons-cli:jar</exclude>
+                <exclude>commons-codec:commons-codec:jar</exclude>
+                <exclude>commons-collections:commons-collections:jar</exclude>
+                <exclude>commons-configuration:commons-configuration:jar</exclude>
+                <exclude>commons-daemon:commons-daemon:jar</exclude>
+                <exclude>commons-dbcp:commons-dbcp:jar</exclude>
+                <exclude>commons-digester:commons-digester:jar</exclude>
+                <exclude>commons-httpclient:commons-httpclient:jar</exclude>
+                <exclude>commons-io:commons-io:jar</exclude>
+                <exclude>commons-jxpath:commons-jxpath:jar</exclude>
+                <exclude>commons-lang:commons-lang:jar</exclude>
+                <exclude>commons-logging:commons-logging:jar</exclude>
+                <exclude>commons-net:commons-net:jar</exclude>
+                <exclude>commons-pool:commons-pool:jar</exclude>
+                <exclude>io.micrometer:micrometer-core:jar</exclude>
+                <exclude>io.netty:netty:jar</exclude>
+                <exclude>io.netty:netty-all:jar</exclude>
+                <exclude>io.netty:netty-buffer:jar</exclude>
+                <exclude>io.netty:netty-codec:jar</exclude>
+                <exclude>io.netty:netty-codec-http:jar</exclude>
+                <exclude>io.netty:netty-common:jar</exclude>
+                <exclude>io.netty:netty-handler:jar</exclude>
+                <exclude>io.netty:netty-transport:jar</exclude>
+                <exclude>io.netty:netty-transport-native-epoll:jar</exclude>
+                <exclude>io.reactivex:rxjava:jar</exclude>
+                <exclude>io.reactivex:rxnetty:jar</exclude>
+                <exclude>io.reactivex:rxnetty-contexts:jar</exclude>
+                <exclude>io.reactivex:rxnetty-servo:jar</exclude>
+                <exclude>javax.activation:activation:jar</exclude>
+                <exclude>javax.annotation:javax.annotation-api:jar</exclude>
+                <exclude>javax.inject:javax.inject:jar</exclude>
+                <exclude>javax.servlet:javax.servlet-api:jar</exclude>
+                <exclude>javax.servlet.jsp:jsp-api:jar</exclude>
+                <exclude>javax.validation:validation-api:jar</exclude>
+                <exclude>javax.websocket:javax.websocket-api:jar</exclude>
+                <exclude>javax.ws.rs:javax.ws.rs-api:jar</exclude>
+                <exclude>javax.xml.bind:jaxb-api:jar</exclude>
+                <exclude>javax.xml.stream:stax-api:jar</exclude>
+                <exclude>joda-time:joda-time:jar</exclude>
+                <exclude>log4j:log4j:jar</exclude>
+                <exclude>mysql:mysql-connector-java:jar</exclude>
+                <exclude>net.databinder.dispatch:dispatch-core_2.11:jar</exclude>
+                <exclude>net.databinder.dispatch:dispatch-json4s-jackson_2.11:jar</exclude>
+                <exclude>org.antlr:antlr-runtime:jar</exclude>
+                <exclude>org.antlr:stringtemplate:jar</exclude>
+                <exclude>org.apache.commons:commons-compress:jar</exclude>
+                <exclude>org.apache.commons:commons-math:jar</exclude>
+                <exclude>org.apache.commons:commons-math3:jar</exclude>
+                <exclude>org.apache.curator:curator-client:jar</exclude>
+                <exclude>org.apache.curator:curator-framework:jar</exclude>
+                <exclude>org.apache.curator:curator-recipes:jar</exclude>
+                <exclude>org.apache.directory.api:api-asn1-api:jar</exclude>
+                <exclude>org.apache.directory.api:api-util:jar</exclude>
+                <exclude>org.apache.directory.server:apacheds-i18n:jar</exclude>
+                <exclude>org.apache.directory.server:apacheds-kerberos-codec:jar</exclude>
+                <exclude>org.apache.hadoop:hadoop-annotations:jar</exclude>
+                <exclude>org.apache.hadoop:hadoop-auth:jar</exclude>
+                <exclude>org.apache.hadoop:hadoop-common:jar</exclude>
+                <exclude>org.apache.hadoop:hadoop-hdfs:jar</exclude>
+                <exclude>org.apache.htrace:htrace-core:jar</exclude>
+                <exclude>org.apache.httpcomponents:httpclient:jar</exclude>
+                <exclude>org.apache.httpcomponents:httpcore:jar</exclude>
+                <exclude>org.apache.logging.log4j:log4j-api:jar</exclude>
+                <exclude>org.apache.logging.log4j:log4j-core:jar</exclude>
+                <exclude>org.apache.logging.log4j:log4j-jul:jar</exclude>
+                <exclude>org.apache.logging.log4j:log4j-slf4j-impl:jar</exclude>
+                <exclude>org.apache.zookeeper:zookeeper:jar</exclude>
+                <exclude>org.aspectj:aspectjweaver:jar</exclude>
+                <exclude>org.bouncycastle:bcpkix-jdk15on:jar</exclude>
+                <exclude>org.bouncycastle:bcprov-jdk15on:jar</exclude>
+                <exclude>org.codehaus.jackson:jackson-jaxrs:jar</exclude>
+                <exclude>org.codehaus.jackson:jackson-xc:jar</exclude>
+                <exclude>org.codehaus.jettison:jettison:jar</exclude>
+                <exclude>org.codehaus.woodstox:stax2-api:jar</exclude>
+                <exclude>org.codehaus.woodstox:woodstox-core-asl:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-annotations:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-client:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-continuation:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-http:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-io:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-jndi:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-plus:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-security:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-server:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-servlet:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-servlets:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-util:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-webapp:jar</exclude>
+                <exclude>org.eclipse.jetty:jetty-xml:jar</exclude>
+                <exclude>org.eclipse.jetty.websocket:javax-websocket-client-impl:jar</exclude>
+                <exclude>org.eclipse.jetty.websocket:javax-websocket-server-impl:jar</exclude>
+                <exclude>org.eclipse.jetty.websocket:websocket-api:jar</exclude>
+                <exclude>org.eclipse.jetty.websocket:websocket-client:jar</exclude>
+                <exclude>org.eclipse.jetty.websocket:websocket-common:jar</exclude>
+                <exclude>org.eclipse.jetty.websocket:websocket-server:jar</exclude>
+                <exclude>org.eclipse.jetty.websocket:websocket-servlet:jar</exclude>
+                <exclude>org.fusesource.leveldbjni:leveldbjni-all:jar</exclude>
+                <exclude>org.glassfish.hk2:class-model:jar</exclude>
+                <exclude>org.glassfish.hk2:config-types:jar</exclude>
+                <exclude>org.glassfish.hk2.external:aopalliance-repackaged:jar</exclude>
+                <exclude>org.glassfish.hk2.external:asm-all-repackaged:jar</exclude>
+                <exclude>org.glassfish.hk2.external:bean-validator:jar</exclude>
+                <exclude>org.glassfish.hk2.external:javax.inject:jar</exclude>
+                <exclude>org.glassfish.hk2:hk2:jar</exclude>
+                <exclude>org.glassfish.hk2:hk2-api:jar</exclude>
+                <exclude>org.glassfish.hk2:hk2-config:jar</exclude>
+                <exclude>org.glassfish.hk2:hk2-core:jar</exclude>
+                <exclude>org.glassfish.hk2:hk2-locator:jar</exclude>
+                <exclude>org.glassfish.hk2:hk2-runlevel:jar</exclude>
+                <exclude>org.glassfish.hk2:hk2-utils:jar</exclude>
+                <exclude>org.glassfish.hk2:osgi-resource-locator:jar</exclude>
+                <exclude>org.glassfish.hk2:spring-bridge:jar</exclude>
+                <exclude>org.glassfish.jersey.bundles:jaxrs-ri:jar</exclude>
+                <exclude>org.glassfish.jersey.bundles.repackaged:jersey-guava:jar</exclude>
+                <exclude>org.glassfish.jersey.containers:jersey-container-servlet:jar</exclude>
+                <exclude>org.glassfish.jersey.containers:jersey-container-servlet-core:jar</exclude>
+                <exclude>org.glassfish.jersey.core:jersey-client:jar</exclude>
+                <exclude>org.glassfish.jersey.core:jersey-common:jar</exclude>
+                <exclude>org.glassfish.jersey.core:jersey-server:jar</exclude>
+                <exclude>org.glassfish.jersey.ext:jersey-entity-filtering:jar</exclude>
+                <exclude>org.glassfish.jersey.ext:jersey-spring3:jar</exclude>
+                <exclude>org.glassfish.jersey.media:jersey-media-jaxb:jar</exclude>
+                <exclude>org.glassfish.jersey.media:jersey-media-json-jackson:jar</exclude>
+                <exclude>org.glassfish.jersey.media:jersey-media-multipart:jar</exclude>
+                <exclude>org.hdrhistogram:HdrHistogram:jar</exclude>
+                <exclude>org.javassist:javassist:jar</exclude>
+                <exclude>org.json4s:json4s-ast_2.11:jar</exclude>
+                <exclude>org.json4s:json4s-core_2.11:jar</exclude>
+                <exclude>org.json4s:json4s-jackson_2.11:jar</exclude>
+                <exclude>org.jsoup:jsoup:jar</exclude>
+                <exclude>org.jvnet.mimepull:mimepull:jar</exclude>
+                <exclude>org.jvnet:tiger-types:jar</exclude>
+                <exclude>org.latencyutils:LatencyUtils:jar</exclude>
+                <exclude>org.mortbay.jasper:apache-el:jar</exclude>
+                <exclude>org.mortbay.jetty:jetty:jar</exclude>
+                <exclude>org.mortbay.jetty:jetty-util:jar</exclude>
+                <exclude>org.ow2.asm:asm-analysis:jar</exclude>
+                <exclude>org.ow2.asm:asm-commons:jar</exclude>
+                <exclude>org.ow2.asm:asm-tree:jar</exclude>
+                <exclude>org.reflections:reflections:jar</exclude>
+                <exclude>org.scala-lang.modules:scala-parser-combinators_2.11:jar</exclude>
+                <exclude>org.scala-lang.modules:scala-xml_2.11:jar</exclude>
+                <exclude>org.scala-lang:scala-compiler:jar</exclude>
+                <exclude>org.scala-lang:scala-library:jar</exclude>
+                <exclude>org.scala-lang:scala-reflect:jar</exclude>
+                <exclude>org.scala-lang:scalap:jar</exclude>
+                <exclude>org.slf4j:jul-to-slf4j:jar</exclude>
+                <exclude>org.slf4j:slf4j-api:jar</exclude>
+                <exclude>org.springframework.boot:spring-boot:jar</exclude>
+                <exclude>org.springframework.boot:spring-boot-actuator:jar</exclude>
+                <exclude>org.springframework.boot:spring-boot-actuator-autoconfigure:jar</exclude>
+                <exclude>org.springframework.boot:spring-boot-autoconfigure:jar</exclude>
+                <exclude>org.springframework.boot:spring-boot-starter:jar</exclude>
+                <exclude>org.springframework.boot:spring-boot-starter-actuator:jar</exclude>
+                <exclude>org.springframework.boot:spring-boot-starter-aop:jar</exclude>
+                <exclude>org.springframework.boot:spring-boot-starter-jetty:jar</exclude>
+                <exclude>org.springframework.boot:spring-boot-starter-json:jar</exclude>
+                <exclude>org.springframework.boot:spring-boot-starter-log4j2:jar</exclude>
+                <exclude>org.springframework.boot:spring-boot-starter-web:jar</exclude>
+                <exclude>org.springframework.cloud:spring-cloud-commons:jar</exclude>
+                <exclude>org.springframework.cloud:spring-cloud-config-client:jar</exclude>
+                <exclude>org.springframework.cloud:spring-cloud-context:jar</exclude>
+                <exclude>org.springframework.cloud:spring-cloud-netflix-archaius:jar</exclude>
+                <exclude>org.springframework.cloud:spring-cloud-netflix-core:jar</exclude>
+                <exclude>org.springframework.cloud:spring-cloud-netflix-eureka-client:jar</exclude>
+                <exclude>org.springframework.cloud:spring-cloud-netflix-ribbon:jar</exclude>
+                <exclude>org.springframework.cloud:spring-cloud-starter:jar</exclude>
+                <exclude>org.springframework.cloud:spring-cloud-starter-config:jar</exclude>
+                <exclude>org.springframework.cloud:spring-cloud-starter-eureka:jar</exclude>
+                <exclude>org.springframework.cloud:spring-cloud-starter-netflix-archaius:jar</exclude>
+                <exclude>org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:jar</exclude>
+                <exclude>org.springframework.cloud:spring-cloud-starter-netflix-ribbon:jar</exclude>
+                <exclude>org.springframework.security:spring-security-crypto:jar</exclude>
+                <exclude>org.springframework.security:spring-security-rsa:jar</exclude>
+                <exclude>org.springframework:spring-aop:jar</exclude>
+                <exclude>org.springframework:spring-beans:jar</exclude>
+                <exclude>org.springframework:spring-context:jar</exclude>
+                <exclude>org.springframework:spring-core:jar</exclude>
+                <exclude>org.springframework:spring-expression:jar</exclude>
+                <exclude>org.springframework:spring-jcl:jar</exclude>
+                <exclude>org.springframework:spring-web:jar</exclude>
+                <exclude>org.springframework:spring-webmvc:jar</exclude>
+                <exclude>org.tukaani:xz:jar</exclude>
+                <exclude>org.yaml:snakeyaml:jar</exclude>
+                <exclude>software.amazon.ion:ion-java:jar</exclude>
+                <exclude>xerces:xercesImpl:jar</exclude>
+                <exclude>xmlenc:xmlenc:jar</exclude>
+                <exclude>xmlpull:xmlpull:jar</exclude>
+                <exclude>xpp3:xpp3_min:jar</exclude>
+            </excludes>
+        </dependencySet>
+    </dependencySets>
+
+    <fileSets>
+    </fileSets>
+
+</assembly>
+
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/LinksMonitorApplication.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/LinksMonitorApplication.java
new file mode 100644
index 0000000..8f503dc
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/LinksMonitorApplication.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor;
+
+import org.apache.linkis.LinkisBaseServerApp;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@EnableScheduling
+@SpringBootApplication
+public class LinksMonitorApplication {
+
+  public static void main(String[] args) throws ReflectiveOperationException {
+    LinkisBaseServerApp.main(args);
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/dao/VersionDao.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/dao/VersionDao.java
new file mode 100644
index 0000000..b4492c9
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/dao/VersionDao.java
@@ -0,0 +1,61 @@
+/*
+ * 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.linkis.monitor.bml.cleaner.dao;
+
+import org.apache.linkis.monitor.bml.cleaner.entity.CleanedResourceVersion;
+import org.apache.linkis.monitor.bml.cleaner.entity.ResourceVersion;
+import org.apache.linkis.monitor.bml.cleaner.vo.CleanResourceVo;
+
+import org.apache.ibatis.annotations.*;
+
+import java.util.Date;
+import java.util.List;
+
+public interface VersionDao {
+
+  @Select(
+      "select resource_id, count(resource_id) as version_count, max(version) as max_version from "
+          + "linkis_ps_bml_resources_version lpbrv where start_time < #{startTime} GROUP BY resource_id HAVING count(resource_id) > #{maxVersionNum} limit #{limitNum}")
+  List<CleanResourceVo> getAllNeedCleanResource(
+      @Param("maxVersionNum") Integer maxVersionNum,
+      @Param("startTime") Date startTime,
+      @Param("limitNum") int num);
+
+  @Select(
+      "select * from linkis_ps_bml_resources_version where resource_id = #{resourceId} and version < #{minKeepVersion} and version <> 'v000001'")
+  List<ResourceVersion> getCleanVersionsByResourceId(
+      @Param("resourceId") String resourceId, @Param("minKeepVersion") String minKeepVersion);
+
+  @Insert({
+    "insert into linkis_ps_bml_cleaned_resources_version(`resource_id`,`file_md5`,`version`,`size`,`start_byte`, `end_byte`,`resource`,`description`,"
+        + "`start_time`,`end_time`,`client_ip`,`updator`,`enable_flag`,`old_resource`) values(#{resourceId},#{fileMd5},#{version},#{size},#{startByte},#{endByte}"
+        + ",#{resource},#{description},#{startTime},#{endTime},#{clientIp},#{updator},#{enableFlag},#{oldResource})"
+  })
+  @Options(useGeneratedKeys = true, keyProperty = "id")
+  void insertCleanResourceVersion(CleanedResourceVersion cleanedResourceVersion);
+
+  @Delete("delete from linkis_ps_bml_resources_version where id=#{id}")
+  void deleteResourceVersionById(@Param("id") long id);
+
+  @Select(
+      "select version from linkis_ps_bml_resources_version where resource_id =#{resourceId} and version <= #{maxVersion} order by version desc limit #{keepNum},1")
+  String getMinKeepVersion(
+      @Param("resourceId") String resourceId,
+      @Param("maxVersion") String maxVersion,
+      @Param("keepNum") int keepNum);
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/entity/CleanedResourceVersion.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/entity/CleanedResourceVersion.java
new file mode 100644
index 0000000..5a0bfcc
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/entity/CleanedResourceVersion.java
@@ -0,0 +1,209 @@
+/*
+ * 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.linkis.monitor.bml.cleaner.entity;
+
+import java.util.Date;
+
+public class CleanedResourceVersion {
+
+  private long id;
+
+  private String resourceId;
+
+  private String fileMd5;
+
+  private String version;
+
+  private long size;
+
+  private String resource;
+
+  private String oldResource;
+
+  private String description;
+
+  private String clientIp;
+
+  private boolean enableFlag;
+
+  private String user;
+
+  private String system;
+
+  private Date startTime;
+
+  private Date endTime;
+
+  private long startByte;
+
+  private long endByte;
+
+  private String updator;
+
+  public String getResourceId() {
+    return resourceId;
+  }
+
+  public void setResourceId(String resourceId) {
+    this.resourceId = resourceId;
+  }
+
+  public String getUser() {
+    return user;
+  }
+
+  public void setUser(String user) {
+    this.user = user;
+  }
+
+  public String getSystem() {
+    return system;
+  }
+
+  public void setSystem(String system) {
+    this.system = system;
+  }
+
+  public String getVersion() {
+    return version;
+  }
+
+  public void setVersion(String version) {
+    this.version = version;
+  }
+
+  public String getResource() {
+    return resource;
+  }
+
+  public void setResource(String resource) {
+    this.resource = resource;
+  }
+
+  public String getOldResource() {
+    return oldResource;
+  }
+
+  public void setOldResource(String oldResource) {
+    this.oldResource = oldResource;
+  }
+
+  public long getId() {
+    return id;
+  }
+
+  public void setId(long id) {
+    this.id = id;
+  }
+
+  public String getFileMd5() {
+    return fileMd5;
+  }
+
+  public void setFileMd5(String fileMd5) {
+    this.fileMd5 = fileMd5;
+  }
+
+  public long getSize() {
+    return size;
+  }
+
+  public void setSize(long size) {
+    this.size = size;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+
+  public String getClientIp() {
+    return clientIp;
+  }
+
+  public void setClientIp(String clientIp) {
+    this.clientIp = clientIp;
+  }
+
+  public boolean isEnableFlag() {
+    return enableFlag;
+  }
+
+  public void setEnableFlag(boolean enableFlag) {
+    this.enableFlag = enableFlag;
+  }
+
+  public long getStartByte() {
+    return startByte;
+  }
+
+  public void setStartByte(long startByte) {
+    this.startByte = startByte;
+  }
+
+  public long getEndByte() {
+    return endByte;
+  }
+
+  public void setEndByte(long endByte) {
+    this.endByte = endByte;
+  }
+
+  public Date getStartTime() {
+    return startTime;
+  }
+
+  public void setStartTime(Date startTime) {
+    this.startTime = startTime;
+  }
+
+  public Date getEndTime() {
+    return endTime;
+  }
+
+  public void setEndTime(Date endTime) {
+    this.endTime = endTime;
+  }
+
+  public String getUpdator() {
+    return updator;
+  }
+
+  public void setUpdator(String updator) {
+    this.updator = updator;
+  }
+
+  public static CleanedResourceVersion copyFromResourceVersion(ResourceVersion resourceVersion) {
+    CleanedResourceVersion cleanedResourceVersion = new CleanedResourceVersion();
+    cleanedResourceVersion.setResourceId(resourceVersion.getResourceId());
+    cleanedResourceVersion.setOldResource(resourceVersion.getResource());
+    cleanedResourceVersion.setFileMd5(resourceVersion.getFileMd5());
+    cleanedResourceVersion.setClientIp(resourceVersion.getClientIp());
+    cleanedResourceVersion.setSize(resourceVersion.getSize());
+    cleanedResourceVersion.setEnableFlag(resourceVersion.getEnableFlag());
+    cleanedResourceVersion.setVersion(resourceVersion.getVersion());
+    cleanedResourceVersion.setStartByte(resourceVersion.getStartByte());
+    cleanedResourceVersion.setEndByte(resourceVersion.getEndByte());
+    cleanedResourceVersion.setStartTime(resourceVersion.getStartTime());
+    cleanedResourceVersion.setEndTime(resourceVersion.getEndTime());
+    return cleanedResourceVersion;
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/entity/ResourceVersion.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/entity/ResourceVersion.java
new file mode 100644
index 0000000..5d297cf
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/entity/ResourceVersion.java
@@ -0,0 +1,206 @@
+/*
+ * 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.linkis.monitor.bml.cleaner.entity;
+
+import java.util.Date;
+
+public class ResourceVersion {
+
+  private long id;
+
+  private String resourceId;
+
+  private String fileMd5;
+
+  private String version;
+
+  private long size;
+
+  private String resource;
+
+  private String description;
+
+  private String clientIp;
+
+  private boolean enableFlag;
+
+  private String user;
+
+  private String system;
+
+  private Date startTime;
+
+  private Date endTime;
+
+  private long startByte;
+
+  private long endByte;
+
+  private String updator;
+
+  public String getResourceId() {
+    return resourceId;
+  }
+
+  public void setResourceId(String resourceId) {
+    this.resourceId = resourceId;
+  }
+
+  public String getUser() {
+    return user;
+  }
+
+  public void setUser(String user) {
+    this.user = user;
+  }
+
+  public String getSystem() {
+    return system;
+  }
+
+  public void setSystem(String system) {
+    this.system = system;
+  }
+
+  public String getVersion() {
+    return version;
+  }
+
+  public void setVersion(String version) {
+    this.version = version;
+  }
+
+  public String getResource() {
+    return resource;
+  }
+
+  public void setResource(String resource) {
+    this.resource = resource;
+  }
+
+  public long getId() {
+    return id;
+  }
+
+  public void setId(long id) {
+    this.id = id;
+  }
+
+  public String getFileMd5() {
+    return fileMd5;
+  }
+
+  public void setFileMd5(String fileMd5) {
+    this.fileMd5 = fileMd5;
+  }
+
+  public long getSize() {
+    return size;
+  }
+
+  public void setSize(long size) {
+    this.size = size;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+
+  public String getClientIp() {
+    return clientIp;
+  }
+
+  public void setClientIp(String clientIp) {
+    this.clientIp = clientIp;
+  }
+
+  public boolean getEnableFlag() {
+    return enableFlag;
+  }
+
+  public void setEnableFlag(boolean enableFlag) {
+    this.enableFlag = enableFlag;
+  }
+
+  public long getStartByte() {
+    return startByte;
+  }
+
+  public void setStartByte(long startByte) {
+    this.startByte = startByte;
+  }
+
+  public long getEndByte() {
+    return endByte;
+  }
+
+  public void setEndByte(long endByte) {
+    this.endByte = endByte;
+  }
+
+  public Date getStartTime() {
+    return startTime;
+  }
+
+  public void setStartTime(Date startTime) {
+    this.startTime = startTime;
+  }
+
+  public Date getEndTime() {
+    return endTime;
+  }
+
+  public void setEndTime(Date endTime) {
+    this.endTime = endTime;
+  }
+
+  public String getUpdator() {
+    return updator;
+  }
+
+  public void setUpdator(String updator) {
+    this.updator = updator;
+  }
+
+  public static ResourceVersion createNewResourceVersion(
+      String resourceId,
+      String resourcePath,
+      String fileMd5,
+      String clientIp,
+      long size,
+      String version,
+      long startByte) {
+    ResourceVersion resourceVersion = new ResourceVersion();
+    resourceVersion.setResourceId(resourceId);
+    resourceVersion.setResource(resourcePath);
+    resourceVersion.setFileMd5(fileMd5);
+    resourceVersion.setClientIp(clientIp);
+    resourceVersion.setSize(size);
+    resourceVersion.setEnableFlag(true);
+    resourceVersion.setVersion(version);
+    resourceVersion.setStartByte(startByte);
+    resourceVersion.setEndByte(startByte + size - 1);
+    resourceVersion.setStartTime(new Date(System.currentTimeMillis()));
+    resourceVersion.setEndTime(new Date(System.currentTimeMillis()));
+    return resourceVersion;
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/service/CleanerService.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/service/CleanerService.java
new file mode 100644
index 0000000..0dfa15f
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/service/CleanerService.java
@@ -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.
+ */
+
+package org.apache.linkis.monitor.bml.cleaner.service;
+
+public interface CleanerService {
+
+  public void run();
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/service/VersionService.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/service/VersionService.java
new file mode 100644
index 0000000..cc1a45c
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/service/VersionService.java
@@ -0,0 +1,37 @@
+/*
+ * 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.linkis.monitor.bml.cleaner.service;
+
+import org.apache.linkis.common.io.FsPath;
+import org.apache.linkis.monitor.bml.cleaner.entity.CleanedResourceVersion;
+import org.apache.linkis.storage.fs.FileSystem;
+
+import java.io.IOException;
+
+public interface VersionService {
+
+  void doMove(
+      FileSystem fs,
+      FsPath srcPath,
+      FsPath destPath,
+      CleanedResourceVersion insertVersion,
+      long delVersionId)
+      throws IOException;
+
+  void moveOnDb(CleanedResourceVersion insertVersion, long delVersionId);
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/service/impl/CleanerServiceImpl.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/service/impl/CleanerServiceImpl.java
new file mode 100644
index 0000000..ec2ed2d
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/service/impl/CleanerServiceImpl.java
@@ -0,0 +1,167 @@
+/*
+ * 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.linkis.monitor.bml.cleaner.service.impl;
+
+import org.apache.linkis.common.io.FsPath;
+import org.apache.linkis.monitor.bml.cleaner.dao.VersionDao;
+import org.apache.linkis.monitor.bml.cleaner.entity.CleanedResourceVersion;
+import org.apache.linkis.monitor.bml.cleaner.entity.ResourceVersion;
+import org.apache.linkis.monitor.bml.cleaner.service.CleanerService;
+import org.apache.linkis.monitor.bml.cleaner.service.VersionService;
+import org.apache.linkis.monitor.bml.cleaner.vo.CleanResourceVo;
+import org.apache.linkis.monitor.constants.Constants;
+import org.apache.linkis.storage.FSFactory;
+import org.apache.linkis.storage.fs.FileSystem;
+import org.apache.linkis.storage.utils.StorageConfiguration;
+import org.apache.linkis.storage.utils.StorageUtils;
+
+import org.apache.commons.io.IOUtils;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Service
+public class CleanerServiceImpl implements CleanerService {
+
+  private final Logger logger = LoggerFactory.getLogger("CleanerServiceImpl");
+
+  private final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
+
+  public static final String VERSION_FORMAT = "%06d";
+  public static final String VERSION_PREFIX = "v";
+  public static final String TRASH_DIR = "/trash";
+
+  private FileSystem fs = null;
+
+  @Autowired private VersionDao versionDao;
+
+  public void setVersionDao(VersionDao versionDao) {
+    this.versionDao = versionDao;
+  }
+
+  private Set<String> cleanedResourceIds = new HashSet<String>();
+
+  Date previous;
+
+  @Autowired VersionService versionService;
+
+  public void clean() {
+    previous =
+        new Date(
+            System.currentTimeMillis()
+                - (Long) Constants.BML_PREVIOUS_INTERVAL_TIME_DAYS().getValue() * 86400 * 1000);
+
+    if ((Integer) Constants.BML_VERSION_MAX_NUM().getValue()
+            - (Integer) Constants.BML_VERSION_KEEP_NUM().getValue()
+        <= 1) {
+      logger.error("conf error need to keep version num > 1");
+      return;
+    }
+    List<CleanResourceVo> needCleanResources = getCleanResources();
+    while (needCleanResources != null && needCleanResources.size() > 0) {
+      logger.info("need cleaned resource count:{}", needCleanResources.size());
+      fs =
+          (FileSystem)
+              FSFactory.getFs(StorageUtils.HDFS, StorageConfiguration.HDFS_ROOT_USER.getValue());
+      for (CleanResourceVo resourceVo : needCleanResources) {
+        String minVersion =
+            versionDao.getMinKeepVersion(
+                resourceVo.getResourceId(),
+                resourceVo.getMaxVersion(),
+                (Integer) Constants.BML_VERSION_KEEP_NUM().getValue() - 1);
+        List<ResourceVersion> cleanVersionList =
+            versionDao.getCleanVersionsByResourceId(resourceVo.getResourceId(), minVersion);
+        // move on hdfs
+        for (ResourceVersion version : cleanVersionList) {
+          FsPath srcPath = new FsPath(version.getResource());
+          // fs放到外层
+          try {
+            fs.init(null);
+            if (!fs.exists(srcPath)) {
+              logger.error("try to move but bml source file:{} not exists!", version.getResource());
+              CleanedResourceVersion cleanedResourceVersion =
+                  CleanedResourceVersion.copyFromResourceVersion(version);
+              cleanedResourceVersion.setResource("");
+              versionService.moveOnDb(cleanedResourceVersion, version.getId());
+              continue;
+            }
+            String destPrefix =
+                version.getResource().substring(0, version.getResource().indexOf("/bml/") + 4);
+            String destPath =
+                destPrefix
+                    + TRASH_DIR
+                    + File.separator
+                    + sdf.format(new Date())
+                    + File.separator
+                    + version.getResourceId()
+                    + "_"
+                    + version.getVersion();
+            FsPath dest = new FsPath(destPath);
+            if (!fs.exists(dest.getParent())) {
+              fs.mkdirs(dest.getParent());
+            }
+            logger.info("begin to mv bml resource:{} to dest:{}", version.getResource(), destPath);
+            CleanedResourceVersion cleanedResourceVersion =
+                CleanedResourceVersion.copyFromResourceVersion(version);
+            cleanedResourceVersion.setResource(destPath);
+            versionService.doMove(fs, srcPath, dest, cleanedResourceVersion, version.getId());
+          } catch (Exception e) {
+            logger.error("failed to mv bml resource:{}", e.getMessage(), e);
+          }
+        }
+
+        cleanedResourceIds.add(resourceVo.getResourceId());
+      }
+      needCleanResources = getCleanResources();
+    }
+  }
+
+  public void run() {
+    logger.info("start to clean.");
+    clean();
+    logger.info("start to shutdown.");
+    shutdown();
+  }
+
+  void shutdown() {
+    IOUtils.closeQuietly(fs);
+  }
+
+  List<CleanResourceVo> getCleanResources() {
+    List<CleanResourceVo> cleanResourceVoList =
+        versionDao.getAllNeedCleanResource(
+            (Integer) Constants.BML_VERSION_MAX_NUM().getValue(),
+            previous,
+            (Integer) Constants.BML_CLEAN_ONCE_RESOURCE_LIMIT_NUM().getValue());
+
+    return cleanResourceVoList.stream()
+        .filter(cleanResourceVo -> !cleanedResourceIds.contains(cleanResourceVo.getResourceId()))
+        .collect(Collectors.toList());
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/service/impl/VersionServiceImpl.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/service/impl/VersionServiceImpl.java
new file mode 100644
index 0000000..be9e5b7
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/service/impl/VersionServiceImpl.java
@@ -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.
+ */
+
+package org.apache.linkis.monitor.bml.cleaner.service.impl;
+
+import org.apache.linkis.common.io.FsPath;
+import org.apache.linkis.monitor.bml.cleaner.dao.VersionDao;
+import org.apache.linkis.monitor.bml.cleaner.entity.CleanedResourceVersion;
+import org.apache.linkis.monitor.bml.cleaner.service.VersionService;
+import org.apache.linkis.storage.fs.FileSystem;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.IOException;
+
+@Service
+public class VersionServiceImpl implements VersionService {
+
+  @Autowired VersionDao versionDao;
+
+  public void setVersionDao(VersionDao versionDao) {
+    this.versionDao = versionDao;
+  }
+
+  @Transactional(rollbackFor = Throwable.class)
+  public void doMove(
+      FileSystem fs,
+      FsPath srcPath,
+      FsPath destPath,
+      CleanedResourceVersion insertVersion,
+      long delVersionId)
+      throws IOException {
+    versionDao.insertCleanResourceVersion(insertVersion);
+    versionDao.deleteResourceVersionById(delVersionId);
+    fs.renameTo(srcPath, destPath);
+  }
+
+  @Transactional
+  public void moveOnDb(CleanedResourceVersion insertVersion, long delVersionId) {
+    versionDao.insertCleanResourceVersion(insertVersion);
+    versionDao.deleteResourceVersionById(delVersionId);
+  }
+
+  public String test() {
+    return "this a test string";
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/vo/CleanResourceVo.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/vo/CleanResourceVo.java
new file mode 100644
index 0000000..4ef205e
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/bml/cleaner/vo/CleanResourceVo.java
@@ -0,0 +1,48 @@
+/*
+ * 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.linkis.monitor.bml.cleaner.vo;
+
+public class CleanResourceVo {
+  private String resourceId;
+  private int versionCount;
+  private String maxVersion;
+
+  public String getResourceId() {
+    return resourceId;
+  }
+
+  public void setResourceId(String resourceId) {
+    this.resourceId = resourceId;
+  }
+
+  public int getVersionCount() {
+    return versionCount;
+  }
+
+  public void setVersionCount(int versionCount) {
+    this.versionCount = versionCount;
+  }
+
+  public String getMaxVersion() {
+    return maxVersion;
+  }
+
+  public void setMaxVersion(String maxVersion) {
+    this.maxVersion = maxVersion;
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/config/ListenerConfig.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/config/ListenerConfig.java
new file mode 100644
index 0000000..eb5c11a
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/config/ListenerConfig.java
@@ -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.
+ */
+
+package org.apache.linkis.monitor.config;
+
+import org.apache.linkis.monitor.until.HttpsUntils;
+import org.apache.linkis.monitor.until.ThreadUtils;
+import org.apache.linkis.monitor.utils.log.LogUtils;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.event.ContextClosedEvent;
+import org.springframework.context.event.EventListener;
+
+import java.io.IOException;
+
+import org.slf4j.Logger;
+
+@Configuration
+public class ListenerConfig {
+
+  private static final Logger logger = LogUtils.stdOutLogger();
+
+  @EventListener
+  private void shutdownEntrance(ContextClosedEvent event) {
+    try {
+      ThreadUtils.executors.shutdown();
+      HttpsUntils.client.close();
+    } catch (IOException e) {
+      logger.error("ListenerConfig error msg  {}", e.getMessage());
+    }
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/config/MonitorConfig.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/config/MonitorConfig.java
new file mode 100644
index 0000000..5b4c2e2
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/config/MonitorConfig.java
@@ -0,0 +1,72 @@
+/*
+ * 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.linkis.monitor.config;
+
+import org.apache.linkis.common.conf.CommonVars;
+import org.apache.linkis.common.conf.Configuration;
+
+public class MonitorConfig {
+
+  public static final String shellPath = Configuration.getLinkisHome() + "/admin/";
+
+  public static final CommonVars<Integer> USER_MODE_TIMEOUT =
+      CommonVars.apply("linkis.monitor.user.timeOut", 300);
+  public static final CommonVars<String> USER_MODE_ENGINE =
+      CommonVars.apply("linkis.monitor.user.enginelist", "[]");
+
+  public static final CommonVars<Double> ECM_TASK_MAJOR =
+      CommonVars.apply("linkis.monitor.ecmResourceTask.major", 0.03);
+  public static final CommonVars<Double> ECM_TASK_MINOR =
+      CommonVars.apply("linkis.monitor.ecmResourceTask.minor", 0.1);
+  public static final CommonVars<String> ECM_TASK_IMURL =
+      CommonVars.apply("linkis.monitor.metrics.imsUrl");
+  public static final CommonVars<String> ECM_TASK_USER_AUTHKEY =
+      CommonVars.apply("linkis.monitor.metrics.userAuthKey");
+
+  public static final CommonVars<Long> JOB_HISTORY_TIME_EXCEED =
+      CommonVars.apply("linkis.monitor.jobhistory.id.timeExceed", 0L);
+
+  public static final CommonVars<Integer> ENTRANCE_TASK_USERTOTAL =
+      CommonVars.apply("linkis.monitor.entranceTask.userTotalTask", 1000);
+  public static final CommonVars<Integer> ENTRANCE_TASK_TOTAL_MAJOR =
+      CommonVars.apply("linkis.monitor.entranceTask.linkisTotalTaskMajor", 50000);
+  public static final CommonVars<Integer> ENTRANCE_TASK_TOTAL_MINOR =
+      CommonVars.apply("linkis.monitor.entranceTask.linkisTotalTaskMinor", 10000);
+  public static final CommonVars<String> ENTRANCE_TASK_USERLIST =
+      CommonVars.apply("linkis.monitor.entranceTask.userlist", "[]");
+
+  public static final CommonVars<Integer> SCHEDULED_CONFIG_NUM =
+      CommonVars.apply("linkis.monitor.scheduled.pool.cores.num", 10);
+
+  public static final CommonVars<Integer> SHELL_TIMEOUT =
+      CommonVars.apply("linkis.monitor.shell.time.out.minute", 30);
+
+  public static final CommonVars<Integer> USER_MODE_INTERFACE_TIMEOUT =
+      CommonVars.apply("linkis.monitor.user.mode.time.out", 30 * 1000);
+
+  public static final CommonVars<String> SOLUTION_URL =
+      CommonVars.apply(
+          "linkis.monitor.jobhistory.solution.url",
+          "https://linkis.apache.org/docs/latest/tuning-and-troubleshooting/error-guide/error-code");
+
+  public static final CommonVars<String> TASK_RUNTIME_TIMEOUT_DESC =
+      CommonVars.apply(
+          "linkis.monitor.jobhistory.task.timeout.desc",
+          "[Linkis任务信息]您好,您在Linkis/DSS提交的任务(任务ID:{0}),已经运行超过{1}h,"
+              + "请关注是否任务正常,如果不正常您可以到Linkis/DSS管理台进行任务的kill,集群信息为BDAP({2})。详细解决方案见Q47:{3} ");
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/config/ScheduledConfig.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/config/ScheduledConfig.java
new file mode 100644
index 0000000..e954122
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/config/ScheduledConfig.java
@@ -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.
+ */
+
+package org.apache.linkis.monitor.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.SchedulingConfigurer;
+import org.springframework.scheduling.config.ScheduledTaskRegistrar;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+@Configuration
+public class ScheduledConfig implements SchedulingConfigurer {
+  @Bean
+  public Executor taskExecutor() {
+    return Executors.newScheduledThreadPool(MonitorConfig.SCHEDULED_CONFIG_NUM.getValue());
+  }
+
+  @Override
+  public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
+    scheduledTaskRegistrar.setScheduler(taskExecutor());
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/entity/EngineEntity.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/entity/EngineEntity.java
new file mode 100644
index 0000000..760c06b
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/entity/EngineEntity.java
@@ -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.
+ */
+
+package org.apache.linkis.monitor.entity;
+
+import java.io.Serializable;
+
+public class EngineEntity implements Serializable {
+
+  private String engineType;
+
+  private String code;
+
+  private String runType;
+
+  public String getEngineType() {
+    return engineType;
+  }
+
+  public void setEngineType(String engineType) {
+    this.engineType = engineType;
+  }
+
+  public String getCode() {
+    return code;
+  }
+
+  public void setCode(String code) {
+    this.code = code;
+  }
+
+  public String getRunType() {
+    return runType;
+  }
+
+  public void setRunType(String runType) {
+    this.runType = runType;
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/entity/EntranceEntity.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/entity/EntranceEntity.java
new file mode 100644
index 0000000..241439b
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/entity/EntranceEntity.java
@@ -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.
+ */
+
+package org.apache.linkis.monitor.entity;
+
+import java.io.Serializable;
+
+public class EntranceEntity implements Serializable {
+
+  private Integer runningtasks;
+
+  private Integer queuedtasks;
+
+  private String alteruser;
+
+  private String username;
+
+  public Integer getQueuedtasks() {
+    return queuedtasks;
+  }
+
+  public void setQueuedtasks(Integer queuedtasks) {
+    this.queuedtasks = queuedtasks;
+  }
+
+  public String getAlteruser() {
+    return alteruser;
+  }
+
+  public void setAlteruser(String alteruser) {
+    this.alteruser = alteruser;
+  }
+
+  public String getUsername() {
+    return username;
+  }
+
+  public void setUsername(String username) {
+    this.username = username;
+  }
+
+  public Integer getRunningtasks() {
+    return runningtasks;
+  }
+
+  public void setRunningtasks(Integer runningtasks) {
+    this.runningtasks = runningtasks;
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/entity/IndexEntity.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/entity/IndexEntity.java
new file mode 100644
index 0000000..2905f8a
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/entity/IndexEntity.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor.entity;
+
+import org.apache.linkis.monitor.constants.Constants;
+
+public class IndexEntity {
+
+  private final String subsystemId = Constants.ALERT_SUB_SYSTEM_ID();
+  private String interfaceName;
+  private String attrGroup;
+  private String attrName;
+  private String hostIp;
+  private String metricValue;
+
+  public String getSubsystemId() {
+    return subsystemId;
+  }
+
+  public String getInterfaceName() {
+    return interfaceName;
+  }
+
+  public void setInterfaceName(String interfaceName) {
+    this.interfaceName = interfaceName;
+  }
+
+  public String getAttrGroup() {
+    return attrGroup;
+  }
+
+  public void setAttrGroup(String attrGroup) {
+    this.attrGroup = attrGroup;
+  }
+
+  public String getAttrName() {
+    return attrName;
+  }
+
+  public void setAttrName(String attrName) {
+    this.attrName = attrName;
+  }
+
+  public String getHostIp() {
+    return hostIp;
+  }
+
+  public void setHostIp(String hostIp) {
+    this.hostIp = hostIp;
+  }
+
+  public String getMetricValue() {
+    return metricValue;
+  }
+
+  public void setMetricValue(String metricValue) {
+    this.metricValue = metricValue;
+  }
+
+  public IndexEntity() {}
+
+  public IndexEntity(
+      String interfaceName, String attrGroup, String attrName, String hostIp, String metricValue) {
+    this.interfaceName = interfaceName;
+    this.attrGroup = attrGroup;
+    this.attrName = attrName;
+    this.hostIp = hostIp;
+    this.metricValue = metricValue;
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/dao/InsLabelRelationDao.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/dao/InsLabelRelationDao.java
new file mode 100644
index 0000000..d805c1a
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/dao/InsLabelRelationDao.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor.instance.dao;
+
+import org.apache.linkis.monitor.instance.entity.InsPersistenceLabel;
+
+import java.util.List;
+
+public interface InsLabelRelationDao {
+
+  /**
+   * Search labels
+   *
+   * @param instance instance value (http:port)
+   * @return
+   */
+  List<InsPersistenceLabel> searchLabelsByInstance(String instance);
+
+  void dropRelationsByInstance(String instance);
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/dao/InstanceInfoDao.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/dao/InstanceInfoDao.java
new file mode 100644
index 0000000..973801a
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/dao/InstanceInfoDao.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor.instance.dao;
+
+import org.apache.linkis.common.ServiceInstance;
+import org.apache.linkis.monitor.instance.entity.InstanceInfo;
+
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface InstanceInfoDao {
+
+  void removeInstance(ServiceInstance instance);
+
+  List<InstanceInfo> getInstanceInfoByApplicationNameAndHostnameFuzzy(
+      @Param("hostname") String hostname, @Param("applicationName") String applicationName);
+
+  List<InstanceInfo> getInstanceInfoByApplicationNameAndInstanceName(
+      @Param("instanceName") String instanceName, @Param("applicationName") String applicationName);
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/dao/InstanceLabelDao.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/dao/InstanceLabelDao.java
new file mode 100644
index 0000000..361bebf
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/dao/InstanceLabelDao.java
@@ -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.
+ */
+
+package org.apache.linkis.monitor.instance.dao;
+
+import org.apache.linkis.monitor.instance.entity.InsPersistenceLabel;
+
+import java.util.List;
+
+public interface InstanceLabelDao {
+  /**
+   * Remove label
+   *
+   * @param label label entity
+   */
+  void remove(InsPersistenceLabel label);
+
+  /**
+   * Remove key -> value map from label id
+   *
+   * @param labelId
+   */
+  void doRemoveKeyValues(Integer labelId);
+
+  void doRemoveKeyValuesBatch(List<Integer> labelIds);
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/entity/InsPersistenceLabel.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/entity/InsPersistenceLabel.java
new file mode 100644
index 0000000..0959c27
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/entity/InsPersistenceLabel.java
@@ -0,0 +1,98 @@
+/*
+ * 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.linkis.monitor.instance.entity;
+
+import java.util.Date;
+
+public class InsPersistenceLabel {
+  private Integer id;
+  private int labelValueSize = -1;
+  private String stringValue;
+  private Boolean modifiable = false;
+
+  private String labelKey;
+
+  private String feature;
+
+  private Date updateTime;
+  private Date createTime;
+
+  public Boolean getModifiable() {
+    return modifiable;
+  }
+
+  public void setModifiable(Boolean modifiable) {
+    this.modifiable = modifiable;
+  }
+
+  public Integer getId() {
+    return id;
+  }
+
+  public void setId(Integer id) {
+    this.id = id;
+  }
+
+  public int getLabelValueSize() {
+    return labelValueSize;
+  }
+
+  public void setLabelValueSize(int labelValueSize) {
+    this.labelValueSize = labelValueSize;
+  }
+
+  public String getStringValue() {
+    return stringValue;
+  }
+
+  public void setStringValue(String stringValue) {
+    this.stringValue = stringValue;
+  }
+
+  public Date getUpdateTime() {
+    return updateTime;
+  }
+
+  public void setUpdateTime(Date updateTime) {
+    this.updateTime = updateTime;
+  }
+
+  public Date getCreateTime() {
+    return createTime;
+  }
+
+  public void setCreateTime(Date createTime) {
+    this.createTime = createTime;
+  }
+
+  public String getLabelKey() {
+    return labelKey;
+  }
+
+  public void setLabelKey(String labelKey) {
+    this.labelKey = labelKey;
+  }
+
+  public String getFeature() {
+    return feature;
+  }
+
+  public void setFeature(String feature) {
+    this.feature = feature;
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/entity/InsPersistenceLabelValue.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/entity/InsPersistenceLabelValue.java
new file mode 100644
index 0000000..029f552
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/entity/InsPersistenceLabelValue.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.linkis.monitor.instance.entity;
+
+public class InsPersistenceLabelValue {
+
+  private Integer labelId;
+
+  private String valueKey;
+
+  private String valueContent;
+
+  public InsPersistenceLabelValue() {}
+
+  public InsPersistenceLabelValue(Integer labelId, String key, String content) {
+    this.labelId = labelId;
+    this.valueKey = key;
+    this.valueContent = content;
+  }
+
+  public String getValueKey() {
+    return valueKey;
+  }
+
+  public void setValueKey(String valueKey) {
+    this.valueKey = valueKey;
+  }
+
+  public String getValueContent() {
+    return valueContent;
+  }
+
+  public void setValueContent(String valueContent) {
+    this.valueContent = valueContent;
+  }
+
+  public Integer getLabelId() {
+    return labelId;
+  }
+
+  public void setLabelId(Integer labelId) {
+    this.labelId = labelId;
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/entity/InstanceInfo.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/entity/InstanceInfo.java
new file mode 100644
index 0000000..5360ffd
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/instance/entity/InstanceInfo.java
@@ -0,0 +1,92 @@
+/*
+ * 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.linkis.monitor.instance.entity;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class InstanceInfo {
+  /** Automatic increment */
+  private Integer id;
+
+  private String applicationName;
+
+  private String instance;
+
+  private Date updateTime;
+
+  private Date createTime;
+  /** Labels related */
+  private List<InsPersistenceLabel> labels = new ArrayList<>();
+
+  public InstanceInfo() {}
+
+  public InstanceInfo(String applicationName, String instance) {
+    this.applicationName = applicationName;
+    this.instance = instance;
+  }
+
+  public List<InsPersistenceLabel> getLabels() {
+    return labels;
+  }
+
+  public void setLabels(List<InsPersistenceLabel> labels) {
+    this.labels = labels;
+  }
+
+  public Integer getId() {
+    return id;
+  }
+
+  public void setId(Integer id) {
+    this.id = id;
+  }
+
+  public String getApplicationName() {
+    return applicationName;
+  }
+
+  public void setApplicationName(String applicationName) {
+    this.applicationName = applicationName;
+  }
+
+  public String getInstance() {
+    return instance;
+  }
+
+  public void setInstance(String instance) {
+    this.instance = instance;
+  }
+
+  public Date getUpdateTime() {
+    return updateTime;
+  }
+
+  public void setUpdateTime(Date updateTime) {
+    this.updateTime = updateTime;
+  }
+
+  public Date getCreateTime() {
+    return createTime;
+  }
+
+  public void setCreateTime(Date createTime) {
+    this.createTime = createTime;
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/QueryUtils.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/QueryUtils.java
new file mode 100644
index 0000000..aa73471
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/QueryUtils.java
@@ -0,0 +1,31 @@
+/*
+ * 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.linkis.monitor.jobhistory;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class QueryUtils {
+
+  private static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+
+  public static String dateToString(Date date) {
+    return dateFormat.format(date);
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/dao/JobHistoryMapper.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/dao/JobHistoryMapper.java
new file mode 100644
index 0000000..ebd213c
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/dao/JobHistoryMapper.java
@@ -0,0 +1,67 @@
+/*
+ * 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.linkis.monitor.jobhistory.dao;
+
+import org.apache.linkis.monitor.jobhistory.entity.JobHistory;
+
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Date;
+import java.util.List;
+
+public interface JobHistoryMapper {
+
+  List<JobHistory> selectJobHistory(JobHistory jobReq);
+
+  List<JobHistory> search(
+      @Param("id") Long id,
+      @Param("umUser") String username,
+      @Param("status") List<String> status,
+      @Param("startDate") Date startDate,
+      @Param("endDate") Date endDate,
+      @Param("engineType") String engineType);
+
+  void updateIncompleteJobStatusGivenIDList(
+      @Param("idList") List<Long> idList, @Param("targetStatus") String targetStatus);
+
+  void updateJobStatusForInstanceGivenStatusList(
+      @Param("instanceName") String instanceName,
+      @Param("statusList") List<String> statusList,
+      @Param("targetStatus") String targetStatus,
+      @Param("startDate") Date startDate);
+
+  List<JobHistory> searchByCache(
+      @Param("id") Long id,
+      @Param("umUser") String username,
+      @Param("status") List<String> status,
+      @Param("startDate") Date startDate,
+      @Param("endDate") Date endDate,
+      @Param("engineType") String engineType);
+
+  List<JobHistory> searchByCacheAndUpdateTime(
+      @Param("id") Long id,
+      @Param("umUser") String username,
+      @Param("status") List<String> status,
+      @Param("startDate") Date startDate,
+      @Param("endDate") Date endDate,
+      @Param("engineType") String engineType);
+
+  Long selectIdByHalfDay(@Param("id") long beginId);
+
+  Long selectMaxId();
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/entity/JobHistory.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/entity/JobHistory.java
new file mode 100644
index 0000000..e2499d3
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/entity/JobHistory.java
@@ -0,0 +1,221 @@
+/*
+ * 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.linkis.monitor.jobhistory.entity;
+
+import org.apache.linkis.monitor.jobhistory.QueryUtils;
+
+import java.util.Date;
+
+public class JobHistory {
+
+  private Long id;
+
+  private String jobReqId;
+
+  private String submitUser;
+
+  private String executeUser;
+
+  private String source;
+
+  private String labels;
+
+  private String params;
+
+  private String progress;
+
+  private String status;
+
+  private String logPath;
+
+  private Integer errorCode;
+
+  private String errorDesc;
+
+  private Date createdTime;
+
+  private Date updatedTime;
+
+  private String updateTimeMills;
+
+  private String instances;
+
+  private String metrics;
+
+  private String engineType;
+
+  private String executionCode;
+
+  private String observeInfo;
+
+  public Long getId() {
+    return id;
+  }
+
+  public void setId(Long id) {
+    this.id = id;
+  }
+
+  public String getJobReqId() {
+    return jobReqId;
+  }
+
+  public void setJobReqId(String jobReqId) {
+    this.jobReqId = jobReqId;
+  }
+
+  public String getSubmitUser() {
+    return submitUser;
+  }
+
+  public void setSubmitUser(String submitUser) {
+    this.submitUser = submitUser;
+  }
+
+  public String getExecuteUser() {
+    return executeUser;
+  }
+
+  public void setExecuteUser(String executeUser) {
+    this.executeUser = executeUser;
+  }
+
+  public String getSource() {
+    return source;
+  }
+
+  public void setSource(String source) {
+    this.source = source;
+  }
+
+  public String getLabels() {
+    return labels;
+  }
+
+  public void setLabels(String labels) {
+    this.labels = labels;
+  }
+
+  public String getParams() {
+    return params;
+  }
+
+  public void setParams(String params) {
+    this.params = params;
+  }
+
+  public String getProgress() {
+    return progress;
+  }
+
+  public void setProgress(String progress) {
+    this.progress = progress;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+  public String getLogPath() {
+    return logPath;
+  }
+
+  public void setLogPath(String logPath) {
+    this.logPath = logPath;
+  }
+
+  public Integer getErrorCode() {
+    return errorCode;
+  }
+
+  public void setErrorCode(Integer errorCode) {
+    this.errorCode = errorCode;
+  }
+
+  public String getErrorDesc() {
+    return errorDesc;
+  }
+
+  public void setErrorDesc(String errorDesc) {
+    this.errorDesc = errorDesc;
+  }
+
+  public Date getCreatedTime() {
+    return createdTime;
+  }
+
+  public void setCreatedTime(Date createdTime) {
+    this.createdTime = createdTime;
+  }
+
+  public Date getUpdatedTime() {
+    return updatedTime;
+  }
+
+  public void setUpdatedTime(Date updatedTime) {
+    this.updatedTime = updatedTime;
+  }
+
+  public String getInstances() {
+    return instances;
+  }
+
+  public void setInstances(String instances) {
+    this.instances = instances;
+  }
+
+  public String getMetrics() {
+    return metrics;
+  }
+
+  public void setMetrics(String metrics) {
+    this.metrics = metrics;
+  }
+
+  public String getEngineType() {
+    return engineType;
+  }
+
+  public void setEngineType(String engineType) {
+    this.engineType = engineType;
+  }
+
+  public String getExecutionCode() {
+    return executionCode;
+  }
+
+  public void setExecutionCode(String executionCode) {
+    this.executionCode = executionCode;
+  }
+
+  public String getUpdateTimeMills() {
+    return QueryUtils.dateToString(getUpdatedTime());
+  }
+
+  public String getObserveInfo() {
+    return observeInfo;
+  }
+
+  public void setObserveInfo(String observeInfo) {
+    this.observeInfo = observeInfo;
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/exception/AnomalyScannerException.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/exception/AnomalyScannerException.java
new file mode 100644
index 0000000..b2c83be
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/exception/AnomalyScannerException.java
@@ -0,0 +1,37 @@
+/*
+ * 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.linkis.monitor.jobhistory.exception;
+
+import org.apache.linkis.common.exception.ErrorException;
+import org.apache.linkis.common.exception.ExceptionLevel;
+
+public class AnomalyScannerException extends ErrorException {
+  public AnomalyScannerException(int errCode, String desc) {
+    super(errCode, desc);
+  }
+
+  public AnomalyScannerException(
+      int errCode, String desc, String ip, int port, String serviceKind) {
+    super(errCode, desc, ip, port, serviceKind);
+  }
+
+  @Override
+  public ExceptionLevel getLevel() {
+    return super.getLevel();
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/exception/DirtyDataCleanException.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/exception/DirtyDataCleanException.java
new file mode 100644
index 0000000..b1c5e64
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/jobhistory/exception/DirtyDataCleanException.java
@@ -0,0 +1,37 @@
+/*
+ * 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.linkis.monitor.jobhistory.exception;
+
+import org.apache.linkis.common.exception.ErrorException;
+import org.apache.linkis.common.exception.ExceptionLevel;
+
+public class DirtyDataCleanException extends ErrorException {
+  public DirtyDataCleanException(int errCode, String desc) {
+    super(errCode, desc);
+  }
+
+  public DirtyDataCleanException(
+      int errCode, String desc, String ip, int port, String serviceKind) {
+    super(errCode, desc, ip, port, serviceKind);
+  }
+
+  @Override
+  public ExceptionLevel getLevel() {
+    return super.getLevel();
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/BmlClear.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/BmlClear.java
new file mode 100644
index 0000000..cb7d371
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/BmlClear.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor.scheduled;
+
+import org.apache.linkis.monitor.bml.cleaner.service.CleanerService;
+import org.apache.linkis.monitor.utils.log.LogUtils;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import org.slf4j.Logger;
+
+@Component
+@PropertySource(value = "classpath:linkis-et-monitor.properties", encoding = "UTF-8")
+public class BmlClear {
+
+  private static final Logger logger = LogUtils.stdOutLogger();
+
+  @Autowired private CleanerService cleanerServices;
+
+  @Scheduled(cron = "${linkis.monitor.bml.clear.history.version.cron}")
+  public void jobHistoryScanTask() {
+    logger.info("start to clear bml history version");
+    cleanerServices.run();
+    logger.info("end to clear bml history version");
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/EcRecordClear.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/EcRecordClear.java
new file mode 100644
index 0000000..ddb3d3e
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/EcRecordClear.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.linkis.monitor.scheduled;
+
+import org.apache.linkis.monitor.config.MonitorConfig;
+import org.apache.linkis.monitor.until.ThreadUtils;
+import org.apache.linkis.monitor.utils.log.LogUtils;
+
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+
+/** * Task: clean up linkis_cg_ec_resource_info_record data */
+@Component
+@PropertySource(value = "classpath:linkis-et-monitor.properties", encoding = "UTF-8")
+public class EcRecordClear {
+
+  private static final Logger logger = LogUtils.stdOutLogger();
+
+  @Scheduled(cron = "${linkis.monitor.clear.ecRecord.cron}")
+  public void ecRecordClear() {
+    logger.info("Start to clear_ec_record shell");
+    List<String> cmdlist = new ArrayList<>();
+    cmdlist.add("sh");
+    cmdlist.add(MonitorConfig.shellPath + "clear_ec_record.sh");
+    logger.info("clear_ec_record  shell command {}", cmdlist);
+    String exec = ThreadUtils.run(cmdlist, "clear_ec_record.sh");
+    logger.info("shell log  {}", exec);
+    logger.info("End to clear_ec_record shell ");
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/EntranceTaskMonitor.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/EntranceTaskMonitor.java
new file mode 100644
index 0000000..a6e7879
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/EntranceTaskMonitor.java
@@ -0,0 +1,195 @@
+/*
+ * 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.linkis.monitor.scheduled;
+
+import org.apache.linkis.common.ServiceInstance;
+import org.apache.linkis.common.conf.Configuration;
+import org.apache.linkis.governance.common.conf.GovernanceCommonConf;
+import org.apache.linkis.monitor.config.MonitorConfig;
+import org.apache.linkis.monitor.constants.Constants;
+import org.apache.linkis.monitor.entity.IndexEntity;
+import org.apache.linkis.monitor.until.HttpsUntils;
+import org.apache.linkis.monitor.utils.alert.AlertDesc;
+import org.apache.linkis.monitor.utils.alert.ims.MonitorAlertUtils;
+import org.apache.linkis.monitor.utils.alert.ims.PooledImsAlertUtils;
+import org.apache.linkis.rpc.Sender;
+import org.apache.linkis.server.BDPJettyServerHelper;
+
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.*;
+
+import com.google.gson.internal.LinkedTreeMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * * Entrance monitors the number of tasks for specified users and systems. If the configured
+ * threshold is exceeded, an alarm will be triggered.
+ */
+@Component
+@PropertySource(value = "classpath:linkis-et-monitor.properties", encoding = "UTF-8")
+public class EntranceTaskMonitor {
+
+  private static final Logger logger = LoggerFactory.getLogger(EntranceTaskMonitor.class);
+
+  private static final String ENTRANCE_RUNNING_TASK = "entrance_running_task";
+  private static final String ENTRANCE_QUEUED_TASK = "entrance_queued_task";
+
+  @Scheduled(cron = "${linkis.monitor.entrance.task.cron}")
+  public void entranceTask() throws IOException {
+    List<LinkedTreeMap<String, String>> userlist = new ArrayList<>();
+    String value = MonitorConfig.ENTRANCE_TASK_USERLIST.getValue();
+    if (StringUtils.isNotBlank(value)) {
+      userlist = BDPJettyServerHelper.gson().fromJson(value, ArrayList.class);
+    }
+
+    userlist.forEach(
+        entranceEntity -> {
+          Map<String, Object> data = new HashMap<>();
+          try {
+            data =
+                MapUtils.getMap(
+                    HttpsUntils.getEntranceTask(null, entranceEntity.get("username"), null),
+                    "data");
+            logger.info("TaskMonitor userlist response  {}:", data);
+          } catch (IOException e) {
+            logger.warn("failed to get EntranceTask data");
+          }
+
+          int runningNumber =
+              null != entranceEntity.get("runningtasks")
+                  ? Integer.parseInt(entranceEntity.get("runningtasks"))
+                  : 0;
+          int queuedNumber =
+              null != entranceEntity.get("queuedtasks")
+                  ? Integer.parseInt(entranceEntity.get("queuedtasks"))
+                  : 0;
+
+          BigDecimal runningtotal = new BigDecimal((int) data.get("runningNumber"));
+          BigDecimal queuedtotal = new BigDecimal((int) data.get("queuedNumber"));
+          BigDecimal total = runningtotal.add(queuedtotal);
+          HashMap<String, String> parms = new HashMap<>();
+          parms.put("$username", entranceEntity.get("username"));
+          parms.put("$alteruser", entranceEntity.get("alteruser"));
+          parms.put("$url", Configuration.GATEWAY_URL().getValue());
+          // 获取标准阈值
+          if (runningtotal.intValue() > runningNumber) {
+            // 触发告警 用户运行任务满
+            parms.put("$runningtask", String.valueOf(runningNumber));
+            Map<String, AlertDesc> ecmResourceAlerts =
+                MonitorAlertUtils.getAlerts(Constants.ALERT_RESOURCE_MONITOR(), parms);
+            PooledImsAlertUtils.addAlert(ecmResourceAlerts.get("12005"));
+          }
+          if (queuedtotal.intValue() > queuedNumber) {
+            // 触发告警 用户排队任务满
+            parms.put("$queuedtask", String.valueOf(queuedNumber));
+            Map<String, AlertDesc> ecmResourceAlerts =
+                MonitorAlertUtils.getAlerts(Constants.ALERT_RESOURCE_MONITOR(), parms);
+            PooledImsAlertUtils.addAlert(ecmResourceAlerts.get("12006"));
+          }
+          int usertotalTask = MonitorConfig.ENTRANCE_TASK_USERTOTAL.getValue();
+          if (total.intValue() > usertotalTask) {
+            parms.put("$tasktotal", String.valueOf(usertotalTask));
+            Map<String, AlertDesc> ecmResourceAlerts =
+                MonitorAlertUtils.getAlerts(Constants.ALERT_RESOURCE_MONITOR(), parms);
+            PooledImsAlertUtils.addAlert(ecmResourceAlerts.get("12007"));
+          }
+        });
+    Map<String, Object> likisData = null;
+    try {
+      likisData = MapUtils.getMap(HttpsUntils.getEntranceTask(null, "hadoop", null), "data");
+      logger.info("TaskMonitor hadoop response  {}:", likisData);
+    } catch (IOException e) {
+      logger.warn("failed to get EntranceTask data");
+    }
+    // 系统监控
+    BigDecimal runningNumber = new BigDecimal((int) likisData.get("runningNumber"));
+    BigDecimal queuedNumber = new BigDecimal((int) likisData.get("queuedNumber"));
+    BigDecimal total = runningNumber.add(queuedNumber);
+
+    HashMap<String, String> parms = new HashMap<>();
+    parms.put("$url", Configuration.GATEWAY_URL().getValue());
+    int linkisTotalMajor = MonitorConfig.ENTRANCE_TASK_TOTAL_MAJOR.getValue();
+    int linkisTotalMinor = MonitorConfig.ENTRANCE_TASK_TOTAL_MINOR.getValue();
+    if (total.intValue() >= linkisTotalMajor) {
+      // 触发告警Major
+      parms.put("$taskmajor", String.valueOf(linkisTotalMajor));
+      logger.info("TaskMonitor parms {}:", parms);
+      Map<String, AlertDesc> ecmResourceAlerts =
+          MonitorAlertUtils.getAlerts(Constants.ALERT_RESOURCE_MONITOR(), parms);
+      PooledImsAlertUtils.addAlert(ecmResourceAlerts.get("12009"));
+
+    } else if (total.intValue() >= linkisTotalMinor) {
+      parms.put("$taskminor", String.valueOf(linkisTotalMinor));
+      logger.info("TaskMonitor parms {}:", parms);
+      Map<String, AlertDesc> ecmResourceAlerts =
+          MonitorAlertUtils.getAlerts(Constants.ALERT_RESOURCE_MONITOR(), parms);
+      PooledImsAlertUtils.addAlert(ecmResourceAlerts.get("12008"));
+    }
+    resourceSendToIms();
+  }
+
+  public static void resourceSendToIms() {
+    ServiceInstance[] instances =
+        Sender.getInstances(GovernanceCommonConf.ENTRANCE_SERVICE_NAME().getValue());
+    if (null != instances) {
+      for (ServiceInstance instance : instances) {
+        String serviceInstance = instance.getInstance();
+        try {
+          Map<String, Object> instanceData =
+              MapUtils.getMap(HttpsUntils.getEntranceTask(null, "hadoop", serviceInstance), "data");
+          int runningNumber = 0;
+          int queuedNumber = 0;
+          if (instanceData.containsKey("runningNumber")) {
+            runningNumber = (int) instanceData.get("runningNumber");
+          }
+          if (instanceData.containsKey("queuedNumber")) {
+            queuedNumber = (int) instanceData.get("queuedNumber");
+          }
+          logger.info("ResourceMonitor send index ");
+          List<IndexEntity> list = new ArrayList<>();
+          list.add(
+              new IndexEntity(
+                  serviceInstance,
+                  "entrance",
+                  ENTRANCE_RUNNING_TASK,
+                  HttpsUntils.localHost,
+                  String.valueOf(runningNumber)));
+          list.add(
+              new IndexEntity(
+                  serviceInstance,
+                  "entrance",
+                  ENTRANCE_QUEUED_TASK,
+                  HttpsUntils.localHost,
+                  String.valueOf(queuedNumber)));
+          HttpsUntils.sendIndex(list);
+        } catch (IOException e) {
+          logger.warn("failed to send EcmResource index :" + e);
+        }
+      }
+    }
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/JobHistoryClear.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/JobHistoryClear.java
new file mode 100644
index 0000000..346ca9c
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/JobHistoryClear.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.linkis.monitor.scheduled;
+
+import org.apache.linkis.monitor.config.MonitorConfig;
+import org.apache.linkis.monitor.until.ThreadUtils;
+import org.apache.linkis.monitor.utils.log.LogUtils;
+
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+
+/** * Task: clean up linkis_ps_job_history_group_history data */
+@Component
+@PropertySource(value = "classpath:linkis-et-monitor.properties", encoding = "UTF-8")
+public class JobHistoryClear {
+
+  private static final Logger logger = LogUtils.stdOutLogger();
+
+  @Scheduled(cron = "${linkis.monitor.clear.history.task.cron}")
+  public void historyTaskClear() {
+    logger.info("Start to clear_history_task shell");
+    List<String> cmdlist = new ArrayList<>();
+    cmdlist.add("sh");
+    cmdlist.add(MonitorConfig.shellPath + "clear_history_task.sh");
+    logger.info("clear_history_task  shell command {}", cmdlist);
+    String exec = ThreadUtils.run(cmdlist, "clear_history_task.sh");
+    logger.info("shell log  {}", exec);
+    logger.info("End to clear_history_task shell ");
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/JobHistoryMonitor.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/JobHistoryMonitor.java
new file mode 100644
index 0000000..c4bd65a
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/JobHistoryMonitor.java
@@ -0,0 +1,265 @@
+/*
+ * 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.linkis.monitor.scheduled;
+
+import org.apache.linkis.monitor.config.MonitorConfig;
+import org.apache.linkis.monitor.constants.Constants;
+import org.apache.linkis.monitor.core.pac.DataFetcher;
+import org.apache.linkis.monitor.core.scanner.AnomalyScanner;
+import org.apache.linkis.monitor.core.scanner.DefaultScanner;
+import org.apache.linkis.monitor.factory.MapperFactory;
+import org.apache.linkis.monitor.jobhistory.JobHistoryDataFetcher;
+import org.apache.linkis.monitor.jobhistory.errorcode.JobHistoryErrCodeRule;
+import org.apache.linkis.monitor.jobhistory.errorcode.JobHistoryErrorCodeAlertSender;
+import org.apache.linkis.monitor.jobhistory.jobtime.JobTimeExceedAlertSender;
+import org.apache.linkis.monitor.jobhistory.jobtime.JobTimeExceedRule;
+import org.apache.linkis.monitor.jobhistory.labels.JobHistoryLabelsAlertSender;
+import org.apache.linkis.monitor.jobhistory.labels.JobHistoryLabelsRule;
+import org.apache.linkis.monitor.jobhistory.runtime.CommonJobRunTimeRule;
+import org.apache.linkis.monitor.jobhistory.runtime.CommonRunTimeAlertSender;
+import org.apache.linkis.monitor.jobhistory.runtime.JobHistoryRunTimeAlertSender;
+import org.apache.linkis.monitor.jobhistory.runtime.JobHistoryRunTimeRule;
+import org.apache.linkis.monitor.until.CacheUtils;
+import org.apache.linkis.monitor.utils.alert.AlertDesc;
+import org.apache.linkis.monitor.utils.alert.ims.ImsAlertDesc;
+import org.apache.linkis.monitor.utils.alert.ims.MonitorAlertUtils;
+import org.apache.linkis.monitor.utils.alert.ims.UserLabelAlertUtils;
+import org.apache.linkis.monitor.utils.log.LogUtils;
+
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+import org.slf4j.Logger;
+
+/**
+ * * jobHistory monitor 1.errorCode: Monitor the error code 2.userLabel: tenant label
+ * monitoring,scan the execution data within the first 20 minutes, and judge the labels field of the
+ * data
+ *
+ * <p>3.jobResultRunTime: Scan the execution data within the first 20 minutes, and judge the
+ * completed tasks. If the parm field in the jobhistory contains (task.notification.conditions) and
+ * the result of executing the task is (Succeed, Failed, Canceled, Timeout, ALL) any one of them, an
+ * alarm is triggered and the result of the job is that it has ended. All three are indispensable
+ *
+ * <p>4.jobResultRunTimeForDSS: Scan the execution data within the first 20 minutes, scan the tasks
+ * that have been marked for notification, if the task has ended, a notification will be initiated
+ *
+ * <p>5.jobHistoryUnfinishedScan: monitor the status of the execution task, scan the data outside 12
+ * hours and within 24 hours
+ */
+@Component
+@PropertySource(value = "classpath:linkis-et-monitor.properties", encoding = "UTF-8")
+public class JobHistoryMonitor {
+
+  private static final Logger logger = LogUtils.stdOutLogger();
+  private static final long backtrackNum = 1000000L;
+
+  @Scheduled(cron = "${linkis.monitor.jobHistory.finished.cron}")
+  public void jobHistoryFinishedScan() {
+    long intervalMs = 20 * 60 * 1000;
+    long maxIntervalMs = Constants.ERRORCODE_MAX_INTERVALS_SECONDS() * 1000;
+    long endTime = System.currentTimeMillis();
+    long startTime = endTime - intervalMs;
+    long realIntervals = Math.min(endTime - startTime, maxIntervalMs);
+    AnomalyScanner scanner = new DefaultScanner();
+    boolean shouldStart = false;
+    long id;
+    if (null == CacheUtils.cacheBuilder.getIfPresent("jobHistoryId")) {
+      long maxId = MapperFactory.getJobHistoryMapper().selectMaxId();
+      long beginId = 0L;
+      if (maxId > backtrackNum) {
+        beginId = maxId - backtrackNum;
+      }
+      id = MapperFactory.getJobHistoryMapper().selectIdByHalfDay(beginId);
+      CacheUtils.cacheBuilder.put("jobHistoryId", id);
+    } else {
+      id = CacheUtils.cacheBuilder.getIfPresent("jobHistoryId");
+    }
+    List<DataFetcher> fetchers = generateFetchersfortime(startTime, endTime, id, "updated_time");
+    if (fetchers.isEmpty()) {
+      logger.warn("generated 0 dataFetchers, plz check input");
+      return;
+    }
+    // errorCode
+    try {
+      Map<String, AlertDesc> errorCodeAlerts =
+          MonitorAlertUtils.getAlerts(Constants.SCAN_PREFIX_ERRORCODE(), null);
+
+      if (errorCodeAlerts == null || errorCodeAlerts.size() == 0) {
+        logger.info("[INFO] Loaded 0 errorcode alert from alert-rule properties file.");
+      } else {
+        logger.info(
+            "[INFO] Loaded {} errorcode alert from alert-rules properties file.",
+            errorCodeAlerts.size());
+        shouldStart = true;
+        addIntervalToImsAlerts(errorCodeAlerts, realIntervals);
+        JobHistoryErrCodeRule jobHistoryErrCodeRule =
+            new JobHistoryErrCodeRule(
+                errorCodeAlerts.keySet(), new JobHistoryErrorCodeAlertSender(errorCodeAlerts));
+        scanner.addScanRule(jobHistoryErrCodeRule);
+      }
+    } catch (Exception e) {
+      logger.warn("Jobhistory Monitor ErrorCode Faily: " + e.getMessage());
+    }
+    // userLabel
+    try {
+      Map<String, AlertDesc> userLabelAlerts =
+          UserLabelAlertUtils.getAlerts(Constants.USER_LABEL_MONITOR(), "");
+      if (userLabelAlerts == null || userLabelAlerts.size() == 0) {
+        logger.info("[INFO] Loaded 0 alerts userLabel alert-rule from alert properties file.");
+      } else {
+        logger.info(
+            "[INFO] Loaded {} alerts userLabel alert-rules from alert properties file.",
+            userLabelAlerts.size());
+        shouldStart = true;
+        JobHistoryLabelsRule jobHistoryLabelsRule =
+            new JobHistoryLabelsRule(new JobHistoryLabelsAlertSender());
+        scanner.addScanRule(jobHistoryLabelsRule);
+      }
+    } catch (Exception e) {
+      logger.warn("Jobhistory Monitor UserLabel Faily: " + e.getMessage());
+    }
+    // jobResultRunTime
+    try {
+      Map<String, AlertDesc> jobResultAlerts =
+          MonitorAlertUtils.getAlerts((Constants.SCAN_PREFIX_ERRORCODE()), null);
+      if (jobResultAlerts == null || jobResultAlerts.size() == 0) {
+        logger.info("[INFO] Loaded 0 jobResult alert from alert-rule properties file.");
+      } else {
+        logger.info(
+            "[INFO] Loaded {} alerts jobResult alert-rules from alert properties file.",
+            jobResultAlerts.size());
+        shouldStart = true;
+        JobHistoryRunTimeRule jobHistoryRunTimeRule =
+            new JobHistoryRunTimeRule(new JobHistoryRunTimeAlertSender());
+        scanner.addScanRule(jobHistoryRunTimeRule);
+      }
+    } catch (Exception e) {
+      logger.warn("Jobhistory Monitor JobResultRunTime Faily: " + e.getMessage());
+    }
+    // jobResultRunTimeForDSS
+    try {
+      Map<String, AlertDesc> dssJobResultAlerts =
+          MonitorAlertUtils.getAlerts((Constants.SCAN_PREFIX_ERRORCODE()), null);
+      if (dssJobResultAlerts == null || dssJobResultAlerts.size() == 0) {
+        logger.info("[INFO] Loaded 0 jobResult alert from alert-rule properties file.");
+      } else {
+        logger.info(
+            "[INFO] Loaded {} alerts jobResult alert-rules from alert properties file.",
+            dssJobResultAlerts.size());
+        shouldStart = true;
+
+        CommonJobRunTimeRule commonJobRunTimeRule =
+            new CommonJobRunTimeRule(new CommonRunTimeAlertSender());
+        scanner.addScanRule(commonJobRunTimeRule);
+      }
+    } catch (Exception e) {
+      logger.warn("Jobhistory JobResultRunTimeForDSS ErrorCode Faily: " + e.getMessage());
+    }
+    run(scanner, fetchers, shouldStart);
+  }
+
+  @Scheduled(cron = "${linkis.monitor.jobHistory.timeout.cron}")
+  public void jobHistoryUnfinishedScan() {
+    long id =
+        Optional.ofNullable(CacheUtils.cacheBuilder.getIfPresent("jobhistoryScan"))
+            .orElse(MonitorConfig.JOB_HISTORY_TIME_EXCEED.getValue());
+    long intervalMs = Constants.TIMEOUT_INTERVALS_SECONDS() * 1000;
+    long maxIntervalMs = Constants.ERRORCODE_MAX_INTERVALS_SECONDS() * 1000;
+    long endTime = System.currentTimeMillis();
+    long startTime = endTime - intervalMs;
+    long realIntervals = Math.min(endTime - startTime, maxIntervalMs);
+    AnomalyScanner scanner = new DefaultScanner();
+    boolean shouldStart = false;
+    List<DataFetcher> fetchers =
+        generateFetchers(startTime, endTime, maxIntervalMs, id, "created_time");
+    if (fetchers.isEmpty()) {
+      logger.warn("generated 0 dataFetchers, plz check input");
+      return;
+    }
+    Map<String, AlertDesc> jobTimeAlerts =
+        MonitorAlertUtils.getAlerts((Constants.SCAN_PREFIX_UNFINISHED_JOBTIME_EXCEED_SEC()), null);
+    if (jobTimeAlerts == null || jobTimeAlerts.size() == 0) {
+      logger.info("[INFO] Loaded 0 alerts jobtime alert-rule from alert properties file.");
+    } else {
+      logger.info(
+          "[INFO] Loaded {} alerts jobtime alert-rules from alert properties file.",
+          jobTimeAlerts.size());
+      shouldStart = true;
+      addIntervalToImsAlerts(jobTimeAlerts, realIntervals);
+      JobTimeExceedRule jobTimeExceedRule =
+          new JobTimeExceedRule(
+              jobTimeAlerts.keySet(), new JobTimeExceedAlertSender(jobTimeAlerts));
+      scanner.addScanRule(jobTimeExceedRule);
+    }
+    run(scanner, fetchers, shouldStart);
+  }
+
+  public static void run(AnomalyScanner scanner, List<DataFetcher> fetchers, Boolean shouldStart) {
+    if (shouldStart) {
+      scanner.addDataFetchers(fetchers);
+      scanner.run();
+    }
+  }
+
+  private static List<DataFetcher> generateFetchers(
+      long startTime, long endTime, long maxIntervalMs, long id, String timeType) {
+    List<DataFetcher> ret = new ArrayList<>();
+    long pe = endTime;
+    long ps;
+    while (pe > startTime) {
+      ps = Math.max(pe - maxIntervalMs, startTime);
+      String[] fetcherArgs =
+          new String[] {String.valueOf(ps), String.valueOf(pe), String.valueOf(id), timeType};
+      ret.add(new JobHistoryDataFetcher(fetcherArgs, MapperFactory.getJobHistoryMapper()));
+      logger.info(
+          "Generated dataFetcher for startTime: " + new Date(ps) + ". EndTime: " + new Date(pe));
+      pe = pe - maxIntervalMs;
+    }
+    return ret;
+  }
+
+  private static List<DataFetcher> generateFetchersfortime(
+      long startTime, long endTime, long id, String timeType) {
+    List<DataFetcher> fetchers = new ArrayList<>();
+    String[] fetcherArgs =
+        new String[] {
+          String.valueOf(startTime), String.valueOf(endTime), String.valueOf(id), timeType
+        };
+    fetchers.add(new JobHistoryDataFetcher(fetcherArgs, MapperFactory.getJobHistoryMapper()));
+    logger.info(
+        "Generated dataFetcher for startTime: "
+            + new Date(startTime)
+            + ". EndTime: "
+            + new Date(endTime));
+    return fetchers;
+  }
+
+  private static void addIntervalToImsAlerts(Map<String, AlertDesc> alerts, long realIntervals) {
+    for (AlertDesc alert : alerts.values()) {
+      if (!(alert instanceof ImsAlertDesc)) {
+        logger.info("[warn]  ignore wrong alert" + alert);
+      } else {
+        ((ImsAlertDesc) alert).hitIntervalMs_$eq(realIntervals);
+      }
+    }
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/ResourceMonitor.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/ResourceMonitor.java
new file mode 100644
index 0000000..ac30465
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/ResourceMonitor.java
@@ -0,0 +1,162 @@
+/*
+ * 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.linkis.monitor.scheduled;
+
+import org.apache.linkis.common.utils.ByteTimeUtils;
+import org.apache.linkis.monitor.config.MonitorConfig;
+import org.apache.linkis.monitor.constants.Constants;
+import org.apache.linkis.monitor.entity.IndexEntity;
+import org.apache.linkis.monitor.until.HttpsUntils;
+import org.apache.linkis.monitor.utils.alert.AlertDesc;
+import org.apache.linkis.monitor.utils.alert.ims.MonitorAlertUtils;
+import org.apache.linkis.monitor.utils.alert.ims.PooledImsAlertUtils;
+
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** * Monitor the usage of ECM resources for monitoring and metrics reporting */
+@Component
+@PropertySource(value = "classpath:linkis-et-monitor.properties", encoding = "UTF-8")
+public class ResourceMonitor {
+
+  private static final Logger logger = LoggerFactory.getLogger(ResourceMonitor.class);
+
+  @Scheduled(cron = "${linkis.monitor.ecm.resource.cron}")
+  public void ecmResourceTask() {
+    Map<String, Object> resultmap = null;
+    AtomicReference<String> tenant = new AtomicReference<>("租户标签:公共资源");
+    AtomicReference<Double> totalMemory = new AtomicReference<>(0.0);
+    AtomicReference<Double> totalInstance = new AtomicReference<>(0.0);
+    AtomicReference<Double> totalCores = new AtomicReference<>(0.0);
+    try {
+      resultmap = HttpsUntils.sendHttp(null, null);
+      logger.info("ResourceMonitor  response  {}:", resultmap);
+    } catch (IOException e) {
+      logger.warn("failed to get EcmResource data");
+    }
+    // got interface data
+    Map<String, List<Map<String, Object>>> data = MapUtils.getMap(resultmap, "data");
+    List<Map<String, Object>> emNodeVoList = data.getOrDefault("EMs", new ArrayList<>());
+    StringJoiner minor = new StringJoiner(",");
+    StringJoiner major = new StringJoiner(",");
+    // deal ecm resource
+    emNodeVoList.forEach(
+        emNodeVo -> {
+          Map<String, Object> leftResource = MapUtils.getMap(emNodeVo, "leftResource");
+          Map<String, Object> maxResource = MapUtils.getMap(emNodeVo, "maxResource");
+          // 新增 ECM资源告警,需补充此ECM所属租户
+          List<Map<String, Object>> labels = (List<Map<String, Object>>) emNodeVo.get("labels");
+          labels.stream()
+              .filter(labelmap -> labelmap.containsKey("tenant"))
+              .forEach(map -> tenant.set("租户标签:" + map.get("stringValue").toString()));
+          String leftmemory =
+              ByteTimeUtils.bytesToString((long) leftResource.getOrDefault("memory", 0));
+          String maxmemory =
+              ByteTimeUtils.bytesToString((long) maxResource.getOrDefault("memory", 0));
+
+          String leftmemoryStr = leftmemory.split(" ")[0];
+          String maxmemoryStr = maxmemory.split(" ")[0];
+
+          BigDecimal leftMemory = new BigDecimal(leftmemoryStr);
+          BigDecimal leftCores = new BigDecimal((int) leftResource.get("cores"));
+          BigDecimal leftInstance = new BigDecimal((int) leftResource.get("instance"));
+          totalMemory.set(totalMemory.get() + leftMemory.doubleValue());
+          totalInstance.set(totalInstance.get() + leftInstance.doubleValue());
+          totalCores.set(totalCores.get() + leftCores.doubleValue());
+
+          BigDecimal maxMemory = new BigDecimal(maxmemoryStr);
+          BigDecimal maxCores = new BigDecimal((int) maxResource.get("cores"));
+          BigDecimal maxInstance = new BigDecimal((int) maxResource.get("instance"));
+          double memorydouble =
+              leftMemory.divide(maxMemory, 2, BigDecimal.ROUND_HALF_DOWN).doubleValue();
+          double coresdouble =
+              leftCores.divide(maxCores, 2, BigDecimal.ROUND_HALF_DOWN).doubleValue();
+          double instancedouble =
+              leftInstance.divide(maxInstance, 2, BigDecimal.ROUND_HALF_DOWN).doubleValue();
+          Double majorValue = MonitorConfig.ECM_TASK_MAJOR.getValue();
+          Double minorValue = MonitorConfig.ECM_TASK_MINOR.getValue();
+          if (((memorydouble) <= majorValue)
+              || ((coresdouble) <= majorValue)
+              || ((instancedouble) <= majorValue)) {
+            major.add(emNodeVo.get("instance").toString());
+          } else if (((memorydouble) < minorValue)
+              || ((coresdouble) < minorValue)
+              || ((instancedouble) < minorValue)) {
+            minor.add(emNodeVo.get("instance").toString());
+          }
+          HashMap<String, String> replaceParm = new HashMap<>();
+          replaceParm.put("$tenant", tenant.get());
+          if (StringUtils.isNotBlank(major.toString())) {
+            replaceParm.put("$instance", major.toString());
+            replaceParm.put("$ratio", majorValue.toString());
+            Map<String, AlertDesc> ecmResourceAlerts =
+                MonitorAlertUtils.getAlerts(Constants.ALERT_RESOURCE_MONITOR(), replaceParm);
+            PooledImsAlertUtils.addAlert(ecmResourceAlerts.get("12004"));
+          }
+          if (StringUtils.isNotBlank(minor.toString())) {
+            replaceParm.put("$instance", minor.toString());
+            replaceParm.put("$ratio", minorValue.toString());
+            Map<String, AlertDesc> ecmResourceAlerts =
+                MonitorAlertUtils.getAlerts(Constants.ALERT_RESOURCE_MONITOR(), replaceParm);
+            PooledImsAlertUtils.addAlert(ecmResourceAlerts.get("12003"));
+          }
+          resourceSendToIms(
+              coresdouble, memorydouble, instancedouble, HttpsUntils.localHost, "USED");
+        });
+    resourceSendToIms(
+        totalCores.get(), totalMemory.get(), totalInstance.get(), HttpsUntils.localHost, "TOTAL");
+  }
+
+  private void resourceSendToIms(
+      Double coresdouble,
+      Double memorydouble,
+      Double instancedouble,
+      String loaclhost,
+      String name) {
+    List<IndexEntity> list = new ArrayList<>();
+    logger.info("ResourceMonitor  send  index ");
+    String core = "ECM_CPU_";
+    String memory = "ECM_MEMORY_";
+    String instance = "ECM_INSTANCE_";
+    list.add(
+        new IndexEntity(core.concat(name), "CPU", "INDEX", loaclhost, String.valueOf(coresdouble)));
+    list.add(
+        new IndexEntity(
+            memory.concat(name), "MEMORY", "INDEX", loaclhost, String.valueOf(memorydouble)));
+    list.add(
+        new IndexEntity(
+            instance.concat(name), "INSTANCE", "INDEX", loaclhost, String.valueOf(instancedouble)));
+    try {
+      HttpsUntils.sendIndex(list);
+    } catch (IOException e) {
+      logger.warn("failed to send EcmResource index");
+    }
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/TaskLogClear.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/TaskLogClear.java
new file mode 100644
index 0000000..6def756
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/TaskLogClear.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.linkis.monitor.scheduled;
+
+import org.apache.linkis.monitor.config.MonitorConfig;
+import org.apache.linkis.monitor.until.ThreadUtils;
+import org.apache.linkis.monitor.utils.log.LogUtils;
+
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+
+/** * Task: clean up logs, file data of ec materials */
+@Component
+@PropertySource(value = "classpath:linkis-et-monitor.properties", encoding = "UTF-8")
+public class TaskLogClear {
+
+  private static final Logger logger = LogUtils.stdOutLogger();
+
+  @Scheduled(cron = "${linkis.monitor.clear.taskLog.cron}")
+  public void taskLogClear() {
+    logger.info("Start to linkis_task_res_log_clear shell");
+    List<String> cmdlist = new ArrayList<>();
+    cmdlist.add("sh");
+    cmdlist.add(MonitorConfig.shellPath + "linkis_task_res_log_clear.sh");
+    logger.info("linkis_task_res_log_clear  shell command {}", cmdlist);
+    String exec = ThreadUtils.run(cmdlist, "linkis_task_res_log_clear.sh");
+    logger.info("shell log  {}", exec);
+    logger.info("End to linkis_task_res_log_clear shell ");
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/UserModeMonitor.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/UserModeMonitor.java
new file mode 100644
index 0000000..ad6f861
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/scheduled/UserModeMonitor.java
@@ -0,0 +1,177 @@
+/*
+ * 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.linkis.monitor.scheduled;
+
+import org.apache.linkis.common.conf.Configuration;
+import org.apache.linkis.common.utils.Utils;
+import org.apache.linkis.governance.common.entity.task.RequestPersistTask;
+import org.apache.linkis.httpclient.dws.config.DWSClientConfig;
+import org.apache.linkis.manager.label.constant.LabelKeyConstant;
+import org.apache.linkis.monitor.config.MonitorConfig;
+import org.apache.linkis.monitor.constants.Constants;
+import org.apache.linkis.monitor.until.HttpsUntils;
+import org.apache.linkis.monitor.utils.alert.AlertDesc;
+import org.apache.linkis.monitor.utils.alert.ims.MonitorAlertUtils;
+import org.apache.linkis.monitor.utils.alert.ims.PooledImsAlertUtils;
+import org.apache.linkis.server.BDPJettyServerHelper;
+import org.apache.linkis.ujes.client.UJESClient;
+import org.apache.linkis.ujes.client.UJESClientImpl;
+import org.apache.linkis.ujes.client.request.GetTableStatisticInfoAction;
+import org.apache.linkis.ujes.client.request.JobSubmitAction;
+import org.apache.linkis.ujes.client.response.GetTableStatisticInfoResult;
+import org.apache.linkis.ujes.client.response.JobExecuteResult;
+import org.apache.linkis.ujes.client.response.JobInfoResult;
+
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.net.SocketTimeoutException;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+import com.google.gson.internal.LinkedTreeMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * * User mode monitoring: regularly trigger scripts to monitor whether the engine status is running
+ * normally
+ */
+@Component
+public class UserModeMonitor {
+
+  private static final Logger logger = LoggerFactory.getLogger(UserModeMonitor.class);
+
+  private static final DWSClientConfig clientConfig = HttpsUntils.dwsClientConfig;
+
+  private static final UJESClient client = new UJESClientImpl(clientConfig);
+
+  @Scheduled(cron = "${linkis.monitor.user.mode.cron}")
+  public void job() {
+    Optional.ofNullable(MonitorConfig.USER_MODE_ENGINE.getValue())
+        .ifPresent(
+            configStr -> {
+              ArrayList<LinkedTreeMap<String, String>> userModeStr =
+                  BDPJettyServerHelper.gson().fromJson(configStr, ArrayList.class);
+              userModeStr.forEach(
+                  engine -> {
+                    // 3. build job and execute
+                    JobExecuteResult jobExecuteResult = toSubmit(engine);
+                    logger.info(
+                        "start run engineType: {},job id : {}",
+                        engine.get("engineType"),
+                        jobExecuteResult.taskID());
+                    HashMap<String, String> parms = new HashMap<>();
+                    parms.put("$engineType", engine.get("engineType"));
+                    parms.put("$url", Configuration.GATEWAY_URL().getValue());
+                    parms.put("$jobId", jobExecuteResult.taskID());
+                    Utils.sleepQuietly(MonitorConfig.USER_MODE_TIMEOUT.getValue() * 1000);
+                    JobInfoResult jobInfo = client.getJobInfo(jobExecuteResult);
+                    if (jobInfo.isCompleted()) {
+                      if (jobInfo.getJobStatus().equals("Failed")) {
+                        logger.info(
+                            "run fail engineType: {},job id : {}",
+                            engine.get("engineType"),
+                            jobExecuteResult.taskID());
+                        RequestPersistTask requestPersistTask = jobInfo.getRequestPersistTask();
+                        parms.put("$errorCode", String.valueOf(requestPersistTask.getErrCode()));
+                        parms.put("$errorMsg", requestPersistTask.getErrDesc());
+                        Map<String, AlertDesc> failedAlerts =
+                            MonitorAlertUtils.getAlerts(Constants.USER_RESOURCE_MONITOR(), parms);
+                        PooledImsAlertUtils.addAlert(failedAlerts.get("12012"));
+                      }
+                    } else {
+                      logger.info(
+                          "run timeout engineType: {},job id : {}",
+                          engine.get("engineType"),
+                          jobExecuteResult.taskID());
+                      Map<String, AlertDesc> alerts =
+                          MonitorAlertUtils.getAlerts(Constants.USER_RESOURCE_MONITOR(), parms);
+                      PooledImsAlertUtils.addAlert(alerts.get("12011"));
+                    }
+                  });
+            });
+  }
+
+  private static JobExecuteResult toSubmit(LinkedTreeMap<String, String> engine) {
+    // 1. build  params
+    // set label map :EngineTypeLabel/UserCreatorLabel/EngineRunTypeLabel/Tenant
+    Map<String, Object> labels = new HashMap<String, Object>();
+    labels.put(
+        LabelKeyConstant.ENGINE_TYPE_KEY, engine.get("engineType")); // required engineType Label
+    labels.put(
+        LabelKeyConstant.USER_CREATOR_TYPE_KEY,
+        engine.get("executeUser") + "-IDE"); // required execute user and creator eg:hadoop-IDE
+    labels.put(LabelKeyConstant.CODE_TYPE_KEY, engine.get("runType")); // required codeType
+    Map<String, Object> startupMap = new HashMap<String, Object>(16);
+    // setting linkis params
+    // startupMap.put("wds.linkis.rm.yarnqueue", "dws");
+    // 2. build jobSubmitAction
+    JobSubmitAction jobSubmitAction =
+        JobSubmitAction.builder()
+            .addExecuteCode(engine.get("code"))
+            .setStartupParams(startupMap)
+            .setUser(engine.get("executeUser")) // submit user
+            .addExecuteUser(engine.get("executeUser")) // execute user
+            .setLabels(labels)
+            .build();
+    // 3. to execute
+    return client.submit(jobSubmitAction);
+  }
+
+  @Scheduled(cron = "${linkis.monitor.user.db.cron:0 0/10 * * * ?}")
+  public void dbJob() {
+    Map<String, Object> properties = new HashMap<>();
+    properties.put("readTimeout", MonitorConfig.USER_MODE_INTERFACE_TIMEOUT.getValue());
+    DWSClientConfig clientConfig = HttpsUntils.createClientConfig(null, properties);
+    UJESClientImpl ujesClient = new UJESClientImpl(clientConfig);
+    GetTableStatisticInfoAction builder =
+        GetTableStatisticInfoAction.builder()
+            .setUser("hadoop")
+            .setDatabase("default")
+            .setTable("dual")
+            .builder();
+    HashMap<String, String> parms = new HashMap<>();
+    try {
+      GetTableStatisticInfoResult tableStatisticInfo = ujesClient.getTableStatisticInfo(builder);
+      if (tableStatisticInfo.getStatus() != 0) {
+        logger.info("元数据查询服务用户态,执行失败,异常信息:" + tableStatisticInfo.getMessage());
+        //                parms.put("$msg", tableStatisticInfo.getMessage());
+        //                Map<String, AlertDesc> failedAlerts =
+        // MonitorAlertUtils.getAlerts(Constants.USER_RESOURCE_MONITOR(), parms);
+        //                PooledImsAlertUtils.addAlert(failedAlerts.get("12017"));
+      }
+    } catch (Exception e) {
+      if (e instanceof SocketTimeoutException) {
+        Integer timeoutValue = MonitorConfig.USER_MODE_INTERFACE_TIMEOUT.getValue();
+        long timeout = TimeUnit.MILLISECONDS.toSeconds(timeoutValue);
+        logger.info("元数据查询服务用户态,执行超时:" + timeout + "秒");
+        //                parms.put("$timeout", String.valueOf(timeout));
+        //                Map<String, AlertDesc> failedAlerts =
+        // MonitorAlertUtils.getAlerts(Constants.USER_RESOURCE_MONITOR(), parms);
+        //                PooledImsAlertUtils.addAlert(failedAlerts.get("12018"));
+      } else {
+        logger.error("元数据查询服务用户态,执行异常:" + e);
+        //                parms.put("$msg", e.getMessage());
+        //                Map<String, AlertDesc> failedAlerts =
+        // MonitorAlertUtils.getAlerts(Constants.USER_RESOURCE_MONITOR(), parms);
+        //                PooledImsAlertUtils.addAlert(failedAlerts.get("12017"));
+      }
+    }
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/until/CacheUtils.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/until/CacheUtils.java
new file mode 100644
index 0000000..a768fde
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/until/CacheUtils.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor.until;
+
+import java.util.concurrent.TimeUnit;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+
+public class CacheUtils {
+
+  public static Cache<String, Long> cacheBuilder =
+      CacheBuilder.newBuilder()
+          .concurrencyLevel(5)
+          .expireAfterAccess(1, TimeUnit.DAYS)
+          .initialCapacity(20)
+          .maximumSize(1000)
+          .recordStats()
+          .build();
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/until/HttpsUntils.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/until/HttpsUntils.java
new file mode 100644
index 0000000..a504a9d
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/until/HttpsUntils.java
@@ -0,0 +1,173 @@
+/*
+ * 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.linkis.monitor.until;
+
+import org.apache.linkis.bml.conf.BmlConfiguration;
+import org.apache.linkis.common.conf.Configuration;
+import org.apache.linkis.common.utils.Utils;
+import org.apache.linkis.httpclient.dws.authentication.TokenAuthenticationStrategy;
+import org.apache.linkis.httpclient.dws.config.DWSClientConfig;
+import org.apache.linkis.httpclient.dws.config.DWSClientConfigBuilder;
+import org.apache.linkis.monitor.client.MonitorHTTPClient;
+import org.apache.linkis.monitor.client.MonitorHTTPClientClientImpl;
+import org.apache.linkis.monitor.config.MonitorConfig;
+import org.apache.linkis.monitor.entity.IndexEntity;
+import org.apache.linkis.monitor.request.EmsListAction;
+import org.apache.linkis.monitor.request.EntranceTaskAction;
+import org.apache.linkis.monitor.response.EntranceTaskResult;
+import org.apache.linkis.server.BDPJettyServerHelper;
+import org.apache.linkis.ujes.client.response.EmsListResult;
+
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+import org.springframework.util.Assert;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HttpsUntils {
+  private static final Logger logger = LoggerFactory.getLogger(HttpsUntils.class);
+
+  public static DWSClientConfig dwsClientConfig = createClientConfig(null, null);
+  //        IOUtils.closeQuietly(client);
+  public static MonitorHTTPClient client = new MonitorHTTPClientClientImpl(dwsClientConfig);
+  public static final String localHost = Utils.getLocalHostname();
+
+  public static Map<String, Object> sendHttp(String url, Map<String, Object> properties)
+      throws IOException {
+    if (null == dwsClientConfig) {
+      dwsClientConfig = createClientConfig(url, properties);
+    }
+    if (null == client) {
+      client = new MonitorHTTPClientClientImpl(dwsClientConfig);
+    }
+    EmsListAction build = EmsListAction.newBuilder().setUser("hadoop").build();
+    EmsListResult result = client.list(build);
+    return result.getResultMap();
+  }
+
+  public static DWSClientConfig createClientConfig(String url, Map<String, Object> properties) {
+    String realUrl = "";
+    if (StringUtils.isBlank(url)) {
+      realUrl = Configuration.getGateWayURL();
+    } else {
+      realUrl = url;
+    }
+    Map<String, Object> parms = new HashMap<>();
+    if (MapUtils.isNotEmpty(properties)) {
+      parms = properties;
+    }
+    int maxConnection =
+        (int)
+            parms.getOrDefault(
+                BmlConfiguration.CONNECTION_MAX_SIZE_SHORT_NAME(),
+                BmlConfiguration.CONNECTION_MAX_SIZE().getValue());
+    int connectTimeout =
+        (int)
+            parms.getOrDefault(
+                BmlConfiguration.CONNECTION_TIMEOUT_SHORT_NAME(),
+                BmlConfiguration.CONNECTION_TIMEOUT().getValue());
+    int readTimeout =
+        (int)
+            parms.getOrDefault(
+                BmlConfiguration.CONNECTION_READ_TIMEOUT_SHORT_NAME(),
+                BmlConfiguration.CONNECTION_READ_TIMEOUT().getValue());
+    String tokenKey =
+        (String)
+            parms.getOrDefault(
+                BmlConfiguration.AUTH_TOKEN_KEY_SHORT_NAME(),
+                BmlConfiguration.AUTH_TOKEN_KEY().getValue());
+    String tokenValue =
+        (String)
+            parms.getOrDefault(
+                BmlConfiguration.AUTH_TOKEN_VALUE_SHORT_NAME(),
+                BmlConfiguration.AUTH_TOKEN_VALUE().getValue());
+
+    DWSClientConfig clientConfig =
+        ((DWSClientConfigBuilder)
+                (DWSClientConfigBuilder.newBuilder()
+                    .addServerUrl(realUrl)
+                    .connectionTimeout(connectTimeout)
+                    .discoveryEnabled(false)
+                    .discoveryFrequency(1, TimeUnit.MINUTES)
+                    .loadbalancerEnabled(false)
+                    .maxConnectionSize(maxConnection)
+                    .retryEnabled(false)
+                    .readTimeout(readTimeout)
+                    .setAuthenticationStrategy(new TokenAuthenticationStrategy())
+                    .setAuthTokenKey(tokenKey)
+                    .setAuthTokenValue(tokenValue)))
+            .setDWSVersion("v1")
+            .build();
+
+    return clientConfig;
+  }
+
+  public static Map<String, Object> getEntranceTask(String url, String user, String Instance)
+      throws IOException {
+    if (null == dwsClientConfig) {
+      dwsClientConfig = createClientConfig(null, null);
+    }
+    if (null == client) {
+      client = new MonitorHTTPClientClientImpl(dwsClientConfig);
+    }
+    EntranceTaskAction build =
+        EntranceTaskAction.newBuilder().setUser(user).setInstance(Instance).build();
+    EntranceTaskResult result = client.entranList(build);
+    return result.getResultMap();
+  }
+
+  public static void sendIndex(List<IndexEntity> list) throws IOException {
+    Map<String, Object> parm = new HashMap<>();
+    parm.put("userAuthKey", MonitorConfig.ECM_TASK_USER_AUTHKEY.getValue());
+    parm.put("metricDataList", list);
+    String json = BDPJettyServerHelper.gson().toJson(parm);
+
+    RequestConfig requestConfig = RequestConfig.DEFAULT;
+    StringEntity entity =
+        new StringEntity(
+            json, ContentType.create(ContentType.APPLICATION_JSON.getMimeType(), "UTF-8"));
+    entity.setContentEncoding("UTF-8");
+
+    HttpPost httpPost = new HttpPost(MonitorConfig.ECM_TASK_IMURL.getValue());
+    httpPost.setConfig(requestConfig);
+    httpPost.setEntity(entity);
+
+    CloseableHttpClient httpClient = HttpClients.createDefault();
+    CloseableHttpResponse execute = httpClient.execute(httpPost);
+    String responseStr = EntityUtils.toString(execute.getEntity(), "UTF-8");
+    Map<String, String> map = BDPJettyServerHelper.gson().fromJson(responseStr, Map.class);
+    logger.info("send index response :{}", map);
+    Assert.isTrue(!"0".equals(map.get("resultCode")), map.get("resultMsg"));
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/until/ThreadUtils.java b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/until/ThreadUtils.java
new file mode 100644
index 0000000..15a2626
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/java/org/apache/linkis/monitor/until/ThreadUtils.java
@@ -0,0 +1,67 @@
+/*
+ * 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.linkis.monitor.until;
+
+import org.apache.linkis.common.utils.Utils;
+import org.apache.linkis.monitor.config.MonitorConfig;
+import org.apache.linkis.monitor.constants.Constants;
+import org.apache.linkis.monitor.utils.alert.AlertDesc;
+import org.apache.linkis.monitor.utils.alert.ims.MonitorAlertUtils;
+import org.apache.linkis.monitor.utils.alert.ims.PooledImsAlertUtils;
+import org.apache.linkis.monitor.utils.log.LogUtils;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.event.ApplicationContextEvent;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import scala.concurrent.ExecutionContextExecutorService;
+
+import org.slf4j.Logger;
+
+public class ThreadUtils extends ApplicationContextEvent {
+
+  private static final Logger logger = LogUtils.stdOutLogger();
+
+  public static ExecutionContextExecutorService executors =
+      Utils.newCachedExecutionContext(5, "alert-pool-thread-", false);
+
+  public ThreadUtils(ApplicationContext source) {
+    super(source);
+  }
+
+  public static String run(List<String> cmdList, String shellName) {
+    FutureTask future = new FutureTask(() -> Utils.exec(cmdList.toArray(new String[2]), -1));
+    executors.submit(future);
+    String msg = "";
+    try {
+      msg = future.get(MonitorConfig.SHELL_TIMEOUT.getValue(), TimeUnit.MINUTES).toString();
+    } catch (TimeoutException e) {
+      logger.info("execute shell time out {}", shellName);
+      HashMap<String, String> parms = new HashMap<>();
+      parms.put("$shellName", shellName);
+      Map<String, AlertDesc> ecmResourceAlerts =
+          MonitorAlertUtils.getAlerts(Constants.THREAD_TIME_OUT_IM(), parms);
+      PooledImsAlertUtils.addAlert(ecmResourceAlerts.get("12014"));
+    } catch (ExecutionException | InterruptedException e) {
+      logger.error("Thread error msg {}", e.getMessage());
+    }
+    return msg;
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/resources/mapper/common/InsLabelRelationMapper.xml b/linkis-extensions/linkis-et-monitor/src/main/resources/mapper/common/InsLabelRelationMapper.xml
new file mode 100644
index 0000000..6c51f6d
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/resources/mapper/common/InsLabelRelationMapper.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ Copyright 2019 WeBank
+  ~
+  ~ Licensed 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 permissios and
+  ~ limitations under the License.
+  -->
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="org.apache.linkis.monitor.instance.dao.InsLabelRelationDao">
+    <resultMap id="instanceInfoMap" type="org.apache.linkis.monitor.instance.entity.InstanceInfo">
+        <result property="id" column="id"/>
+        <result property="instance" column="instance"/>
+        <result property="applicationName" column="name"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="createTime" column="create_time"/>
+    </resultMap>
+    <resultMap id="instanceInfoCascadeMap" type="org.apache.linkis.monitor.instance.entity.InstanceInfo">
+        <result property="id" column="id"/>
+        <result property="instance" column="instance"/>
+        <result property="applicationName" column="name"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="createTime" column="create_time"/>
+        <collection property="labels" column="instance" select="selectLabelsByIns" ofType="org.apache.linkis.monitor.instance.entity.InsPersistenceLabel"/>
+    </resultMap>
+
+    <resultMap id="insPersistenceLabelMap" type="org.apache.linkis.monitor.instance.entity.InsPersistenceLabel">
+        <result property="id" column="id"/>
+        <result property="labelKey" column="label_key"/>
+        <result property="stringValue" column="label_value"/>
+        <result property="feature" column="label_feature"/>
+        <result property="labelValueSize" column="label_value_size"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="createTime" column="create_time"/>
+    </resultMap>
+
+    <sql id="label_search_columns">
+        l.`id`, l.`label_key`, l.`label_value`, l.`label_feature`,
+        l.`label_value_size`, l.`update_time`, l.`create_time`
+    </sql>
+
+    <sql id="search_ins_columns">
+        s.`id`, s.`instance`, s.`name`, s.`update_time`, s.`create_time`
+    </sql>
+
+    <select id="searchLabelsByInstance" resultMap="insPersistenceLabelMap">
+        <![CDATA[SELECT ]]>
+        <include refid="label_search_columns"/>
+        <![CDATA[ FROM `linkis_ps_instance_label` l
+        INNER JOIN `linkis_ps_instance_label_relation` r
+        ON l.`id` = r.`label_id` AND r.`service_instance` = #{instance};]]>
+    </select>
+
+    <delete id="dropRelationsByInstance">
+        <![CDATA[DELETE FROM `linkis_ps_instance_label_relation` WHERE `service_instance` = #{instance};]]>
+    </delete>
+
+</mapper>
\ No newline at end of file
diff --git a/linkis-extensions/linkis-et-monitor/src/main/resources/mapper/common/InstanceInfoMapper.xml b/linkis-extensions/linkis-et-monitor/src/main/resources/mapper/common/InstanceInfoMapper.xml
new file mode 100644
index 0000000..d5309a5
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/resources/mapper/common/InstanceInfoMapper.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ Copyright 2019 WeBank
+  ~
+  ~ Licensed 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.
+  -->
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="org.apache.linkis.monitor.instance.dao.InstanceInfoDao">
+
+    <resultMap id="instanceInfoMap" type="org.apache.linkis.monitor.instance.entity.InstanceInfo">
+        <result property="id" column="id"/>
+        <result property="instance" column="instance"/>
+        <result property="applicationName" column="name"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="createTime" column="create_time"/>
+    </resultMap>
+
+    <sql id="label_search_columns">
+        `id`, `instance`, `name`, `update_time`,
+        `create_time`
+    </sql>
+
+    <delete id="removeInstance" parameterType="org.apache.linkis.common.ServiceInstance">
+        DELETE FROM linkis_ps_instance_info WHERE instance = #{instance}
+    </delete>
+
+    <select id="getInstanceInfoByApplicationNameAndHostnameFuzzy" parameterType="org.apache.linkis.common.ServiceInstance" resultMap="instanceInfoMap">
+        <bind name="pattern" value="hostname + '%'" />
+        <![CDATA[SELECT ]]>
+        <include refid="label_search_columns"/>
+        <![CDATA[ FROM `linkis_ps_instance_info`
+        WHERE instance LIKE #{pattern} AND name = #{applicationName} ORDER BY update_time DESC;]]>
+    </select>
+
+    <select id="getInstanceInfoByApplicationNameAndInstanceName" parameterType="org.apache.linkis.common.ServiceInstance" resultMap="instanceInfoMap">
+        <![CDATA[SELECT ]]>
+        <include refid="label_search_columns"/>
+        <![CDATA[ FROM `linkis_ps_instance_info`
+        WHERE instance = #{instanceName} AND name = #{applicationName} ORDER BY update_time DESC;]]>
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git a/linkis-extensions/linkis-et-monitor/src/main/resources/mapper/common/InstanceLabelMapper.xml b/linkis-extensions/linkis-et-monitor/src/main/resources/mapper/common/InstanceLabelMapper.xml
new file mode 100644
index 0000000..e7c7558
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/resources/mapper/common/InstanceLabelMapper.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ Copyright 2019 WeBank
+  ~
+  ~ Licensed 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.
+  -->
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="org.apache.linkis.monitor.instance.dao.InstanceLabelDao">
+
+    <resultMap id="insPersistenceLabelMap" type="org.apache.linkis.monitor.instance.entity.InsPersistenceLabel">
+        <result property="id" column="id"/>
+        <result property="labelKey" column="label_key"/>
+        <result property="stringValue" column="label_value"/>
+        <result property="feature" column="label_feature"/>
+        <result property="labelValueSize" column="label_value_size"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="createTime" column="create_time"/>
+    </resultMap>
+
+    <sql id="label_search_columns">
+        `id`, `label_key`, `label_value`, `label_feature`,
+        `label_value_size`, `update_time`, `create_time`
+    </sql>
+
+    <delete id="remove">
+        <choose>
+            <when test="id != null and id != ''">
+                <![CDATA[DELETE FROM `linkis_ps_instance_label` WHERE `id` = #{id}]]>
+            </when>
+            <otherwise>
+                <![CDATA[DELETE FROM `linkis_ps_instance_label` WHERE `label_key` = #{labelKey}
+        AND `label_value` = #{stringValue}]]>
+            </otherwise>
+        </choose>
+    </delete>
+
+    <delete id="doRemoveKeyValues">
+        <![CDATA[DELETE FROM `linkis_ps_instance_label_value_relation` WHERE `label_id` = #{labelId}]]>
+    </delete>
+
+    <delete id="doRemoveKeyValuesBatch">
+        <foreach collection="list" item="item" open="" close="">
+            <![CDATA[DELETE FROM `linkis_ps_instance_label_value_relation` WHERE `label_id` = #{item}]]>
+        </foreach>
+    </delete>
+</mapper>
\ No newline at end of file
diff --git a/linkis-extensions/linkis-et-monitor/src/main/resources/mapper/common/JobHistoryMapper.xml b/linkis-extensions/linkis-et-monitor/src/main/resources/mapper/common/JobHistoryMapper.xml
new file mode 100644
index 0000000..730e8a4
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/resources/mapper/common/JobHistoryMapper.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ Copyright 2019 WeBank
+  ~
+  ~ Licensed 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.
+  -->
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+
+
+<mapper namespace="org.apache.linkis.monitor.jobhistory.dao.JobHistoryMapper">
+
+    <resultMap id="jobHistoryMap" type="org.apache.linkis.monitor.jobhistory.entity.JobHistory"
+               autoMapping="false">
+        <id property="id" column="id"/>
+        <result property="jobReqId" column="job_req_id"/>
+        <result property="submitUser" column="submit_user"/>
+        <result property="executeUser" column="execute_user"/>
+        <result property="source" column="source"/>
+        <result property="labels" column="labels"/>
+        <result property="params" column="params"/>
+        <result property="progress" column="progress"/>
+        <result property="status" column="status"/>
+        <result property="logPath" column="log_path"/>
+        <result property="errorCode" column="error_code"/>
+        <result property="errorDesc" column="error_desc"/>
+        <result property="createdTime" column="created_time"/>
+        <result property="updatedTime" column="updated_time"/>
+        <result property="instances" column="instances"/>
+        <result property="metrics" column="metrics"/>
+        <result property="engineType" column="engine_type"/>
+        <result property="executionCode" column="execution_code"/>
+        <result property="observeInfo" column="observe_info"/>
+    </resultMap>
+
+    <sql id="jobhistory_query">
+        `id`,`job_req_id`,`submit_user`,`execute_user`,`labels`,`params`,`status`,`error_code`,`created_time`,
+        `updated_time`,`instances`,`observe_info`
+    </sql>
+
+    <select id="selectJobHistory" useCache="false" resultMap="jobHistoryMap"
+            parameterType="org.apache.linkis.monitor.jobhistory.entity.JobHistory">
+        SELECT * FROM linkis_ps_job_history_group_history
+        <where>
+            <if test="id != null">id = #{id}</if>
+            <if test="job_req_id != null">and job_req_id = #{job_req_id}</if>
+            <if test="submit_user != null">and submit_user = #{submit_user}</if>
+            <if test="execute_user != null">and execute_user = #{execute_user}</if>
+            <!--            <if test="source != null">and source = #{source}</if>-->
+            <!--            <if test="labels != null">and labels = #{labels}</if>-->
+            <!--            <if test="params != null">and params = #{params}</if>-->
+            <if test="progress != null">and progress = #{progress}</if>
+            <if test="status != null">and status = #{status}</if>
+            <!--            <if test="log_path != null">and log_path = #{log_path}</if>-->
+            <!--            <if test="error_code != null">and error_code = #{error_code}</if>-->
+            <!--            <if test="error_desc != null">and error_desc = #{error_desc}</if>-->
+            <if test="created_time != null">and created_time = #{created_time}</if>
+            <if test="updated_time != null">and updated_time = #{updated_time}</if>
+            <if test="instances != null">and instances = #{instances}</if>
+            <!--            <if test="metrics != null">and metrics = #{metrics}</if>-->
+            <if test="engine_type != null">and engine_type = #{engine_type}</if>
+            <!--            <if test="execution_code != null">and execution_code = #{execution_code }</if>-->
+        </where>
+    </select>
+
+    <select id="search" useCache="true" resultMap="jobHistoryMap">
+        SELECT
+        <include refid="jobhistory_query"/>
+        FROM linkis_ps_job_history_group_history
+        <where>
+            <if test="id != null">id = #{id}</if>
+            <if test="umUser != null">and submit_user = #{umUser}</if>
+            <if test="engineType != null">and engine_type = #{engineType}</if>
+            <if test="startDate != null">and created_time >= #{startDate} AND created_time <![CDATA[<=]]>#{endDate}
+            </if>
+            <if test="status != null">and
+                <foreach collection="status" item="element" close=")" separator="," open="status in (">
+                    #{element}
+                </foreach>
+            </if>
+        </where>
+        ORDER BY linkis_ps_job_history_group_history.created_time DESC
+    </select>
+
+    <update id="updateIncompleteJobStatusGivenIDList">
+        UPDATE linkis_ps_job_history_group_history
+        <set>
+            status = #{targetStatus}
+        </set>
+        <where>
+            <foreach collection="list" index="index" item="element" close=")" separator="," open="id in (">
+                #{element}
+            </foreach>
+        </where>
+    </update>
+
+    <update id="updateJobStatusForInstanceGivenStatusList">
+        UPDATE linkis_ps_job_history_group_history
+        <set>
+            status = #{targetStatus}, error_code=21304, error_desc='Automatically killed because entrance is dead'
+        </set>
+        <where>
+            created_time >= #{startDate}
+            AND instances = #{instanceName}
+            AND
+            <foreach collection="statusList" index="index" item="element" close=")" separator="," open="status in (">
+                #{element}
+            </foreach>
+        </where>
+        LIMIT 5000
+    </update>
+
+    <select id="searchByCache" useCache="true" resultMap="jobHistoryMap">
+        SELECT
+        <include refid="jobhistory_query"/>
+        FROM linkis_ps_job_history_group_history
+        <where>
+            <if test="id != null">id > #{id}</if>
+            <if test="umUser != null">and submit_user = #{umUser}</if>
+            <if test="engineType != null">and engine_type = #{engineType}</if>
+            <if test="startDate != null">and created_time >= #{startDate} AND created_time <![CDATA[<=]]>#{endDate}
+            </if>
+            <if test="status != null">and
+                <foreach collection="status" item="element" close=")" separator="," open="status in (">
+                    #{element}
+                </foreach>
+            </if>
+        </where>
+        ORDER BY linkis_ps_job_history_group_history.id
+    </select>
+
+    <select id="searchByCacheAndUpdateTime" useCache="true" resultMap="jobHistoryMap">
+        SELECT
+        <include refid="jobhistory_query"/>
+        FROM linkis_ps_job_history_group_history
+        <where>
+            <if test="id != null">id > #{id}</if>
+            <if test="umUser != null">and submit_user = #{umUser}</if>
+            <if test="engineType != null">and engine_type = #{engineType}</if>
+            <if test="startDate != null">and updated_time >= #{startDate} AND updated_time <![CDATA[<=]]>#{endDate}
+            </if>
+            <if test="status != null">and
+                <foreach collection="status" item="element" close=")" separator="," open="status in (">
+                    #{element}
+                </foreach>
+            </if>
+        </where>
+        ORDER BY linkis_ps_job_history_group_history.id
+    </select>
+
+    <select id="selectIdByHalfDay" resultType="java.lang.Long">
+        SELECT MIN(id) FROM linkis_ps_job_history_group_history
+        <where>
+            updated_time >= date_sub(now(),interval 12 hour)
+            <if test="id != null">and id > #{id}</if>
+        </where>
+    </select>
+
+    <select id="selectMaxId" resultType="java.lang.Long">
+        SELECT MAX(id) FROM linkis_ps_job_history_group_history
+    </select>
+</mapper>
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/LinkisJobHistoryScanSpringConfiguration.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/LinkisJobHistoryScanSpringConfiguration.scala
new file mode 100644
index 0000000..4154661
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/LinkisJobHistoryScanSpringConfiguration.scala
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor
+
+import org.apache.linkis.monitor.factory.MapperFactory
+import org.apache.linkis.monitor.instance.dao.InstanceInfoDao
+import org.apache.linkis.monitor.jobhistory.dao.JobHistoryMapper
+
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.context.annotation.{ComponentScan, Configuration}
+
+import javax.annotation.PostConstruct
+
+@Configuration
+@ComponentScan(Array("org.apache.linkis.monitor.scan", "org.apache.linkis.mybatis"))
+class LinkisJobHistoryScanSpringConfiguration {
+
+  @Autowired
+  private var jobHistoryMapper: JobHistoryMapper = _
+
+  @Autowired
+  private var instanceInfoMapper: InstanceInfoDao = _
+
+  @PostConstruct
+  def init(): Unit = {
+    MapperFactory.setJobHistoryMapper(jobHistoryMapper)
+    MapperFactory.setInstanceInfoMapper(instanceInfoMapper)
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/client/MonitorHTTPClient.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/client/MonitorHTTPClient.scala
new file mode 100644
index 0000000..4caccd7
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/client/MonitorHTTPClient.scala
@@ -0,0 +1,117 @@
+/*
+ * 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.linkis.monitor.client
+
+import org.apache.linkis.httpclient.authentication.AuthenticationStrategy
+import org.apache.linkis.httpclient.dws.authentication.StaticAuthenticationStrategy
+import org.apache.linkis.httpclient.dws.config.{DWSClientConfig, DWSClientConfigBuilder}
+import org.apache.linkis.httpclient.response.Result
+import org.apache.linkis.monitor.request.{EmsListAction, EntranceTaskAction, MonitorResourceAction}
+import org.apache.linkis.monitor.response.EntranceTaskResult
+import org.apache.linkis.ujes.client.response.EmsListResult
+
+import java.io.Closeable
+import java.util.concurrent.TimeUnit
+
+abstract class MonitorHTTPClient extends Closeable {
+
+  protected[client] def executeJob(ujesJobAction: MonitorResourceAction): Result
+
+  def list(emsListAction: EmsListAction): EmsListResult = {
+    executeJob(emsListAction).asInstanceOf[EmsListResult]
+  }
+
+  def entranList(entranceTaskAction: EntranceTaskAction): EntranceTaskResult = {
+    executeJob(entranceTaskAction).asInstanceOf[EntranceTaskResult]
+  }
+
+}
+
+object MonitorHTTPClient {
+
+  def apply(clientConfig: DWSClientConfig): MonitorHTTPClient = new MonitorHTTPClientClientImpl(
+    clientConfig
+  )
+
+  def apply(serverUrl: String): MonitorHTTPClient = apply(serverUrl, 30000, 10)
+
+  def apply(serverUrl: String, readTimeout: Int, maxConnection: Int): MonitorHTTPClient =
+    apply(serverUrl, readTimeout, maxConnection, new StaticAuthenticationStrategy, "v1")
+
+  def apply(
+      serverUrl: String,
+      readTimeout: Int,
+      maxConnection: Int,
+      authenticationStrategy: AuthenticationStrategy,
+      dwsVersion: String
+  ): MonitorHTTPClient = {
+    val clientConfig = DWSClientConfigBuilder
+      .newBuilder()
+      .addServerUrl(serverUrl)
+      .connectionTimeout(30000)
+      .discoveryEnabled(false)
+      .loadbalancerEnabled(false)
+      .maxConnectionSize(maxConnection)
+      .retryEnabled(false)
+      .readTimeout(readTimeout)
+      .setAuthenticationStrategy(authenticationStrategy)
+      .setDWSVersion(dwsVersion)
+      .build()
+    apply(clientConfig)
+  }
+
+  def getDiscoveryClient(serverUrl: String): MonitorHTTPClient =
+    getDiscoveryClient(serverUrl, 30000, 10)
+
+  def getDiscoveryClient(
+      serverUrl: String,
+      readTimeout: Int,
+      maxConnection: Int
+  ): MonitorHTTPClient =
+    getDiscoveryClient(
+      serverUrl,
+      readTimeout,
+      maxConnection,
+      new StaticAuthenticationStrategy,
+      "v1"
+    )
+
+  def getDiscoveryClient(
+      serverUrl: String,
+      readTimeout: Int,
+      maxConnection: Int,
+      authenticationStrategy: AuthenticationStrategy,
+      dwsVersion: String
+  ): MonitorHTTPClient = {
+    val clientConfig = DWSClientConfigBuilder
+      .newBuilder()
+      .addServerUrl(serverUrl)
+      .connectionTimeout(30000)
+      .discoveryEnabled(true)
+      .discoveryFrequency(1, TimeUnit.MINUTES)
+      .loadbalancerEnabled(true)
+      .maxConnectionSize(maxConnection)
+      .retryEnabled(false)
+      .readTimeout(readTimeout)
+      .setAuthenticationStrategy(authenticationStrategy)
+      .setDWSVersion(dwsVersion)
+      .build()
+    apply(clientConfig)
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/client/MonitorHTTPClientClientImpl.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/client/MonitorHTTPClientClientImpl.scala
new file mode 100644
index 0000000..5554701
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/client/MonitorHTTPClientClientImpl.scala
@@ -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.
+ */
+
+package org.apache.linkis.monitor.client
+
+import org.apache.linkis.httpclient.dws.DWSHttpClient
+import org.apache.linkis.httpclient.dws.config.DWSClientConfig
+import org.apache.linkis.httpclient.request.Action
+import org.apache.linkis.httpclient.response.Result
+import org.apache.linkis.monitor.request.MonitorResourceAction
+
+class MonitorHTTPClientClientImpl(clientConfig: DWSClientConfig) extends MonitorHTTPClient {
+
+  private val dwsHttpClient =
+    new DWSHttpClient(clientConfig, "Linkis-MonitorResource-Execution-Thread")
+
+  override protected[client] def executeJob(ujesJobAction: MonitorResourceAction): Result =
+    ujesJobAction match {
+
+      case action: Action => dwsHttpClient.execute(action)
+
+    }
+
+  override def close(): Unit = dwsHttpClient.close()
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/client/MonitorResourceClient.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/client/MonitorResourceClient.scala
new file mode 100644
index 0000000..d0660e1
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/client/MonitorResourceClient.scala
@@ -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.linkis.monitor.client
+
+import org.apache.linkis.httpclient.authentication.AuthenticationStrategy
+import org.apache.linkis.httpclient.dws.authentication.StaticAuthenticationStrategy
+import org.apache.linkis.httpclient.dws.config.{DWSClientConfig, DWSClientConfigBuilder}
+import org.apache.linkis.httpclient.response.Result
+import org.apache.linkis.monitor.request.{EmsListAction, MonitorResourceAction}
+import org.apache.linkis.ujes.client.response.EmsListResult
+
+import java.io.Closeable
+import java.util.concurrent.TimeUnit
+
+abstract class MonitorResourceClient extends Closeable {
+
+  protected[client] def executeJob(ujesJobAction: MonitorResourceAction): Result
+
+  def list(jobListAction: EmsListAction): EmsListResult = {
+    executeJob(jobListAction).asInstanceOf[EmsListResult]
+  }
+
+}
+
+object MonitorResourceClient {
+
+  def apply(clientConfig: DWSClientConfig): MonitorResourceClient = new MonitorResourceClientImpl(
+    clientConfig
+  )
+
+  def apply(serverUrl: String): MonitorResourceClient = apply(serverUrl, 30000, 10)
+
+  def apply(serverUrl: String, readTimeout: Int, maxConnection: Int): MonitorResourceClient =
+    apply(serverUrl, readTimeout, maxConnection, new StaticAuthenticationStrategy, "v1")
+
+  def apply(
+      serverUrl: String,
+      readTimeout: Int,
+      maxConnection: Int,
+      authenticationStrategy: AuthenticationStrategy,
+      dwsVersion: String
+  ): MonitorResourceClient = {
+    val clientConfig = DWSClientConfigBuilder
+      .newBuilder()
+      .addServerUrl(serverUrl)
+      .connectionTimeout(30000)
+      .discoveryEnabled(false)
+      .loadbalancerEnabled(false)
+      .maxConnectionSize(maxConnection)
+      .retryEnabled(false)
+      .readTimeout(readTimeout)
+      .setAuthenticationStrategy(authenticationStrategy)
+      .setDWSVersion(dwsVersion)
+      .build()
+    apply(clientConfig)
+  }
+
+  def getDiscoveryClient(serverUrl: String): MonitorResourceClient =
+    getDiscoveryClient(serverUrl, 30000, 10)
+
+  def getDiscoveryClient(
+      serverUrl: String,
+      readTimeout: Int,
+      maxConnection: Int
+  ): MonitorResourceClient =
+    getDiscoveryClient(
+      serverUrl,
+      readTimeout,
+      maxConnection,
+      new StaticAuthenticationStrategy,
+      "v1"
+    )
+
+  def getDiscoveryClient(
+      serverUrl: String,
+      readTimeout: Int,
+      maxConnection: Int,
+      authenticationStrategy: AuthenticationStrategy,
+      dwsVersion: String
+  ): MonitorResourceClient = {
+    val clientConfig = DWSClientConfigBuilder
+      .newBuilder()
+      .addServerUrl(serverUrl)
+      .connectionTimeout(30000)
+      .discoveryEnabled(true)
+      .discoveryFrequency(1, TimeUnit.MINUTES)
+      .loadbalancerEnabled(true)
+      .maxConnectionSize(maxConnection)
+      .retryEnabled(false)
+      .readTimeout(readTimeout)
+      .setAuthenticationStrategy(authenticationStrategy)
+      .setDWSVersion(dwsVersion)
+      .build()
+    apply(clientConfig)
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/client/MonitorResourceClientImpl.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/client/MonitorResourceClientImpl.scala
new file mode 100644
index 0000000..06cff3b
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/client/MonitorResourceClientImpl.scala
@@ -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.
+ */
+
+package org.apache.linkis.monitor.client
+
+import org.apache.linkis.httpclient.dws.DWSHttpClient
+import org.apache.linkis.httpclient.dws.config.DWSClientConfig
+import org.apache.linkis.httpclient.request.Action
+import org.apache.linkis.httpclient.response.Result
+import org.apache.linkis.monitor.request.MonitorResourceAction
+
+class MonitorResourceClientImpl(clientConfig: DWSClientConfig) extends MonitorResourceClient {
+
+  private val dwsHttpClient =
+    new DWSHttpClient(clientConfig, "Linkis-MonitorResource-Execution-Thread")
+
+  override protected[client] def executeJob(ujesJobAction: MonitorResourceAction): Result =
+    ujesJobAction match {
+
+      case action: Action => dwsHttpClient.execute(action)
+
+    }
+
+  override def close(): Unit = dwsHttpClient.close()
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/constants/Constants.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/constants/Constants.scala
new file mode 100644
index 0000000..04a0438
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/constants/Constants.scala
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor.constants
+
+import org.apache.linkis.common.conf.CommonVars
+
+object Constants {
+
+  val SCAN_PREFIX_ERRORCODE = "jobhistory.errorcode."
+  val SCAN_PREFIX_UNFINISHED_JOBTIME_EXCEED_SEC = "jobhistory.unfinished.time.exceed.sec."
+  val ALERT_RESOURCE_MONITOR = "ecm.resource.monitor.im."
+
+  val UNFINISHED_JOB_STATUS =
+    "Inited,WaitForRetry,Scheduled,Running".split(",").map(s => s.toUpperCase())
+
+  val FINISHED_JOB_STATUS =
+    "Succeed,Failed,Cancelled,Timeout".split(",").map(s => s.toUpperCase())
+
+  val DATA_FINISHED_JOB_STATUS_ARRAY = "Succeed,Failed,Cancelled,Timeout".split(",")
+
+  val DATA_UNFINISHED_JOB_STATUS_ARRAY =
+    "Inited,WaitForRetry,Scheduled,Running".split(",")
+
+  val ALERT_PROPS_FILE_PATH = CommonVars.properties.getProperty(
+    "linkis.alert.conf.file.path",
+    "linkis-et-monitor-file.properties"
+  )
+
+  val ALERT_IMS_URL = CommonVars.properties.getProperty(
+    "linkis.alert.url",
+    "http://127.0.0.1:10812/ims_data_access/send_alarm.do"
+  )
+
+  val ALERT_SUB_SYSTEM_ID =
+    CommonVars.properties.getProperty("linkis.alert.sub_system_id", "10001")
+
+  val ALERT_DEFAULT_RECEIVERS = CommonVars.properties
+    .getProperty("linkis.alert.receiver.default", "")
+    .split(",")
+    .toSet[String]
+
+  val ALERT_IMS_MAX_LINES = CommonVars[Int]("linkis.alert.content.max.lines", 8).getValue
+
+  val TIMEOUT_INTERVALS_SECONDS =
+    CommonVars[Long]("linkis.monitor.scanner.timeout.interval.seconds", 1 * 60 * 60).getValue
+
+  val ERRORCODE_MAX_INTERVALS_SECONDS =
+    CommonVars[Long]("linkis.errorcode.scanner.max.interval.seconds", 1 * 60 * 60).getValue
+
+  val SCAN_RULE_UNFINISHED_JOB_STATUS =
+    "Inited,WaitForRetry,Scheduled,Running".split(",").map(s => s.toUpperCase())
+
+  val USER_LABEL_MONITOR = "jobhistory.label.monitor.im."
+
+  val USER_LABEL_TENANT: CommonVars[String] =
+    CommonVars[String]("linkis.monitor.jobhistory.userLabel.tenant", "{}")
+
+  val USER_RESOURCE_MONITOR = "user.mode.monitor.im."
+  val BML_CLEAR_IM = "bml.clear.monitor.im."
+  val THREAD_TIME_OUT_IM = "thread.monitor.timeout.im."
+  val JOB_RESULT_IM = "jobhistory.result.monitor.im."
+
+  val BML_VERSION_MAX_NUM: CommonVars[Int] =
+    CommonVars[Int]("linkis.monitor.bml.cleaner.version.max.num", 50)
+
+  val BML_VERSION_KEEP_NUM: CommonVars[Int] =
+    CommonVars[Int]("linkis.monitor.bml.cleaner.version.keep.num", 20)
+
+  val BML_PREVIOUS_INTERVAL_TIME_DAYS: CommonVars[Long] =
+    CommonVars[Long]("linkis.monitor.bml.cleaner.previous.interval.days", 30)
+
+  val BML_CLEAN_ONCE_RESOURCE_LIMIT_NUM: CommonVars[Int] =
+    CommonVars[Int]("linkis.monitor.bml.cleaner.once.limit.num", 100)
+
+  val BML_TRASH_PATH_PREFIX: CommonVars[String] =
+    CommonVars[String]("linkis.monitor.bml.trash.prefix.path", "hdfs:///tmp/linkis/trash/bml_trash")
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/constants/ScanOperatorEnum.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/constants/ScanOperatorEnum.scala
new file mode 100644
index 0000000..02fafa5
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/constants/ScanOperatorEnum.scala
@@ -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.
+ */
+
+package org.apache.linkis.monitor.constants
+
+object ScanOperatorEnum extends Enumeration {
+  type ScanOperatorEnum = Value
+  val BML_VERSION, JOB_HISTORY = Value
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/ob/Event.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/ob/Event.scala
new file mode 100644
index 0000000..bf0508f
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/ob/Event.scala
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor.core.ob
+
+trait Event {
+  def isRegistered: Boolean
+
+  def register(observer: Observer): Unit
+
+  def unRegister(observer: Observer): Unit
+
+  def notifyObserver(event: Event, message: Any): Unit
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/ob/Observer.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/ob/Observer.scala
new file mode 100644
index 0000000..aecc9f7
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/ob/Observer.scala
@@ -0,0 +1,26 @@
+/*
+ * 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.linkis.monitor.core.ob
+
+trait Observer {
+
+  /**
+   * Observer Pattern
+   */
+  def update(event: Event, msg: Any): Unit
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/ob/SingleObserverEvent.java b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/ob/SingleObserverEvent.java
new file mode 100644
index 0000000..0414266
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/ob/SingleObserverEvent.java
@@ -0,0 +1,43 @@
+/*
+ * 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.linkis.monitor.core.ob;
+
+
+public class SingleObserverEvent implements Event {
+    private Observer observer;
+
+    @Override
+    public boolean isRegistered() {
+        return observer != null;
+    }
+
+    @Override
+    public void register(Observer observer) {
+        this.observer = observer;
+    }
+
+    @Override
+    public void unRegister(Observer observer) {
+        this.observer = null;
+    }
+
+    @Override
+    public void notifyObserver(Event event, Object message) {
+        observer.update(event, message);
+    }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/AbstractDataFetcher.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/AbstractDataFetcher.scala
new file mode 100644
index 0000000..21a7598
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/AbstractDataFetcher.scala
@@ -0,0 +1,29 @@
+/*
+ * 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.linkis.monitor.core.pac
+
+abstract class AbstractDataFetcher(customName: String = "") extends DataFetcher {
+
+  private val name: String = if (!customName.isEmpty) {
+    customName
+  } else {
+    this.getClass.getName + "@" + Integer.toHexString(this.hashCode)
+  }
+
+  def getName(): String = this.name
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/AbstractScanRule.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/AbstractScanRule.scala
new file mode 100644
index 0000000..eedf532
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/AbstractScanRule.scala
@@ -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.linkis.monitor.core.pac
+
+import org.apache.linkis.common.utils.Logging
+import org.apache.linkis.monitor.core.ob.{Event, Observer}
+import org.apache.linkis.monitor.core.ob.Observer
+
+abstract class AbstractScanRule(customName: String = "", event: Event, observer: Observer)
+    extends ScanRule
+    with Logging {
+  event.register(observer)
+
+  private val name: String = if (!customName.isEmpty) {
+    customName
+  } else {
+    this.getClass.getName + "@" + Integer.toHexString(this.hashCode)
+  }
+
+  def getName(): String = this.name
+
+  /**
+   * register an observer to trigger if this rule is matched
+   *
+   * @param observer
+   */
+  override def addObserver(observer: Observer): Unit = event.register(observer)
+
+  /**
+   * return registered event
+   *
+   * @return
+   */
+  override def getHitEvent(): Event = event
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/BaseScannedData.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/BaseScannedData.scala
new file mode 100644
index 0000000..3597eb7
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/BaseScannedData.scala
@@ -0,0 +1,26 @@
+/*
+ * 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.linkis.monitor.core.pac
+
+import java.util
+
+class BaseScannedData(owner: String, data: util.List[scala.Any]) extends ScannedData {
+  override def getOwner(): String = this.owner
+
+  override def getData(): util.List[scala.Any] = this.data
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/DataFetcher.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/DataFetcher.scala
new file mode 100644
index 0000000..3b86ce6
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/DataFetcher.scala
@@ -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.
+ */
+
+package org.apache.linkis.monitor.core.pac
+
+import java.util
+
+/**
+ * ScanOperator should encapsulate lower-level client for accessing data from an arbitrary
+ * datasource. e.g. if we want to scan a DB table. Then operator should encapsulate a DAO
+ */
+trait DataFetcher {
+  def getName(): String
+
+  /**
+   * get arguments for querying data
+   *
+   * @return
+   */
+  def getArgs(): Array[scala.Any]
+
+  /**
+   * make a query to mysql/hive etc. given args
+   */
+  def getData(): util.List[scala.Any]
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/ScanBuffer.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/ScanBuffer.scala
new file mode 100644
index 0000000..8518738
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/ScanBuffer.scala
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor.core.pac
+
+import java.util
+import java.util.concurrent.LinkedBlockingDeque
+
+class ScanBuffer {
+  val buffer: LinkedBlockingDeque[ScannedData] = new LinkedBlockingDeque[ScannedData]
+
+  def write(data: ScannedData): Unit = buffer.add(data)
+
+  def write(data: util.List[ScannedData]): Unit = buffer.addAll(data)
+
+  def drain(maxSize: Int = -1): util.List[ScannedData] = {
+    val ret = new util.LinkedList[ScannedData]
+    val realSize = if (maxSize < 0) {
+      buffer.size
+    } else {
+      maxSize
+    }
+    buffer.drainTo(ret, realSize)
+    return ret
+  }
+
+  def size(): Int = buffer.size()
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/ScanRule.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/ScanRule.scala
new file mode 100644
index 0000000..fa599c4
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/ScanRule.scala
@@ -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.linkis.monitor.core.pac
+
+import org.apache.linkis.monitor.core.ob.{Event, Observer}
+import org.apache.linkis.monitor.core.ob.Observer
+
+import java.util
+
+trait ScanRule {
+
+  def getName(): String
+
+  /**
+   * register an observer to trigger if this rule is matched
+   *
+   * @param observer
+   */
+  def addObserver(observer: Observer): Unit
+
+  /**
+   * return registered event
+   *
+   * @return
+   */
+  def getHitEvent(): Event
+
+  /**
+   * if data match the pattern, return true and trigger observer should call isMatched()
+   *
+   * @param data
+   * @return
+   */
+  def triggerIfMatched(data: util.List[ScannedData]): Boolean
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/ScannedData.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/ScannedData.scala
new file mode 100644
index 0000000..2c4c1e0
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/pac/ScannedData.scala
@@ -0,0 +1,26 @@
+/*
+ * 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.linkis.monitor.core.pac
+
+import java.util
+
+trait ScannedData {
+  def getOwner(): String
+
+  def getData(): util.List[scala.Any]
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/scanner/AbstractScanner.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/scanner/AbstractScanner.scala
new file mode 100644
index 0000000..4f207cd
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/scanner/AbstractScanner.scala
@@ -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.linkis.monitor.core.scanner
+
+import org.apache.linkis.common.utils.Logging
+import org.apache.linkis.monitor.core.ob.{Event, Observer}
+import org.apache.linkis.monitor.core.pac._
+import org.apache.linkis.monitor.jobhistory.exception.AnomalyScannerException
+
+import java.util
+import java.util.concurrent.CopyOnWriteArrayList
+import java.util.concurrent.atomic.AtomicInteger
+
+abstract class AbstractScanner extends AnomalyScanner with Logging {
+  private val buffer: ScanBuffer = new ScanBuffer
+
+  private val dataFetcherIdx: AtomicInteger =
+    new AtomicInteger(0) // mark next fetcher for sequentially produce data
+
+  private val dataFetcherList: CopyOnWriteArrayList[DataFetcher] =
+    new CopyOnWriteArrayList[DataFetcher]
+
+  private val scanRuleList: CopyOnWriteArrayList[ScanRule] = new CopyOnWriteArrayList[ScanRule]
+
+  /**
+   * Producer
+   */
+  override def addDataFetcher(fetcher: DataFetcher): Unit = {
+    if (fetcher != null) {
+      dataFetcherList.add(fetcher)
+    } else {
+      logger.warn("ignore null DataFetcher")
+    }
+  }
+
+  override def addDataFetchers(fetchers: util.List[DataFetcher]): Unit = {
+    if (fetchers != null && fetchers.size != 0) {
+      dataFetcherList.addAll(fetchers)
+    } else {
+      logger.warn("ignore null or empty DataFetcher")
+    }
+  }
+
+  override def getDataFetchers: util.List[DataFetcher] = dataFetcherList
+
+  /**
+   * directly feed data to buffer
+   */
+  override def feedData(data: util.List[ScannedData]): Unit = {
+    if (data != null && data.size != 0) {
+      buffer.write(data)
+    } else {
+      logger.warn("Fed with null or empty data")
+    }
+  }
+
+  /**
+   * Returns a buffer that allows read/write simultaneously buffer is allowed to be written by other
+   * thread
+   */
+  override def getBuffer(): ScanBuffer = buffer
+
+  /**
+   * add rules to scanner
+   */
+  override def addScanRule(rule: ScanRule): Unit = {
+    if (rule != null) {
+      scanRuleList.add(rule)
+    } else {
+      logger.warn("ignore null ScanRule")
+    }
+  }
+
+  override def addScanRules(rules: util.List[ScanRule]): Unit = {
+    if (rules != null && rules.size != 0) {
+      scanRuleList.addAll(rules)
+    } else {
+      logger.warn("ignore null or empty ScanRule")
+    }
+  }
+
+  override def getScanRules(): util.List[ScanRule] = scanRuleList
+
+  /**
+   * blocking call, scan and analyze until all dataFetchers are accessed once
+   */
+  override def run(): Unit = {
+    if (dataFetcherList.size() == 0) {
+      throw new AnomalyScannerException(21304, "attempting to run scanner with empty dataFetchers")
+    }
+    if (buffer == null) {
+      throw new AnomalyScannerException(21304, "attempting to run scanner with null buffer")
+    }
+    if (scanRuleList.size == 0) {
+      throw new AnomalyScannerException(21304, "attempting to run scanner with empty rules")
+    }
+    while (dataFetcherIdx.get() < dataFetcherList.size()) {
+      scanOneIteration()
+      analyzeOneIteration()
+    }
+  }
+
+  /**
+   *   1. scan data for 1 iteration 2. should be a blocking call 3. see if [[ScanRule]] is matched
+   *      4. trigger [[Event]] and inform observer
+   */
+  override def scanOneIteration(): Unit = {
+    val idx = dataFetcherIdx.getAndIncrement()
+    val fetcher = dataFetcherList.get(idx)
+    if (fetcher != null) {
+      val rawData = fetcher.getData()
+      logger.info("scanned " + rawData.size + " data. Rule: " + fetcher.getName);
+      if (rawData != null && rawData.size != 0) {
+        buffer.write(new BaseScannedData(fetcher.getName, rawData))
+      }
+    } else {
+      logger.warn("ignored null fetcher!!")
+    }
+  }
+
+  /**
+   *   1. should be a blocking call 2. read from [[ScanBuffer]] 2. see if [[ScanRule]] is matched 3.
+   *      trigger [[Observer]]
+   */
+  override def analyzeOneIteration(): Unit = {
+    val dataToAnalyze = buffer.drain()
+    if (dataToAnalyze != null && dataToAnalyze.size() != 0) {
+      val len = scanRuleList.size()
+      for (i <- 0 until len) {
+        val scanRule = scanRuleList.get(i)
+        if (scanRule != null) {
+          logger.info("analyzing " + dataToAnalyze.size + " data. Rule: " + scanRule.getName)
+          scanRule.triggerIfMatched(dataToAnalyze)
+        } else {
+          logger.warn("found empty or null ScanRule")
+        }
+      }
+    } else {
+      logger.info("analyzed 0 data.")
+    }
+  }
+
+  /**
+   *   1. should be non-blocking 2. keeps calling scanOneIteration() and analyzeOneIteration() utils
+   *      stop() is called
+   */
+  override def start(): Unit = {}
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/scanner/AnomalyScanner.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/scanner/AnomalyScanner.scala
new file mode 100644
index 0000000..7fa84d3
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/scanner/AnomalyScanner.scala
@@ -0,0 +1,95 @@
+/*
+ * 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.linkis.monitor.core.scanner
+
+import org.apache.linkis.monitor.core.ob.Event
+import org.apache.linkis.monitor.core.pac.{DataFetcher, ScanBuffer, ScannedData, ScanRule}
+import org.apache.linkis.monitor.core.pac.DataFetcher
+
+import java.util
+
+/**
+ * A Scanner that:
+ *   1. scan a datasource using [[DataFetcher]], write data into [[ScanBuffer]] 2. read data from
+ *      [[ScanBuffer]] see if [[ScanRule]] is matched 3. trigger [[Event]] in [[ScanRule]] and
+ *      inform observer
+ */
+trait AnomalyScanner {
+
+  /**
+   * Producer
+   */
+  def addDataFetcher(dataFetcher: DataFetcher): Unit
+
+  def addDataFetchers(dataFetchers: util.List[DataFetcher]): Unit
+
+  def getDataFetchers: util.List[DataFetcher]
+
+  /**
+   * directly feed data to buffer
+   */
+  def feedData(data: util.List[ScannedData]): Unit
+
+  /**
+   * Buffer
+   */
+
+  /**
+   * add rules to scanner
+   */
+  def addScanRule(rule: ScanRule): Unit
+
+  def addScanRules(rules: util.List[ScanRule]): Unit
+
+  /**
+   * Consumer
+   */
+
+  def getScanRules(): util.List[ScanRule]
+
+  /**
+   * scan and analyze for 1 iteration
+   */
+  def run(): Unit
+
+  /**
+   *   1. should be non-blocking 2. keeps calling scan() utils stop() is called
+   */
+  def start(): Unit
+
+  def shutdown(): Unit
+
+  /**
+   *   1. should be a blocking call 2. call [[DataFetcher]] to read data 3. write result to
+   *      [[ScanBuffer]]
+   */
+  protected def scanOneIteration(): Unit
+
+  /**
+   * Returns a buffer that allows read/write simultaneously buffer is allowed to be written by other
+   * thread
+   */
+  protected def getBuffer(): ScanBuffer
+
+  /**
+   *   1. should be a blocking call 2. read from [[ScanBuffer]] 2. see if [[ScanRule]] is matched 3.
+   *      trigger [[[[org.apache.linkis.tools.core.ob.Observer]]]]
+   */
+  protected def analyzeOneIteration(): Unit
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/scanner/DefaultScanner.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/scanner/DefaultScanner.scala
new file mode 100644
index 0000000..80ab7a5
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/core/scanner/DefaultScanner.scala
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor.core.scanner
+
+import org.apache.linkis.monitor.utils.alert.ims.PooledImsAlertUtils
+
+class DefaultScanner extends AbstractScanner {
+
+  override def shutdown(): Unit = {
+    PooledImsAlertUtils.shutDown(true, -1)
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/factory/MapperFactory.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/factory/MapperFactory.scala
new file mode 100644
index 0000000..eb503c5
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/factory/MapperFactory.scala
@@ -0,0 +1,61 @@
+/*
+ * 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.linkis.monitor.factory
+
+import org.apache.linkis.monitor.instance.dao.{
+  InsLabelRelationDao,
+  InstanceInfoDao,
+  InstanceLabelDao
+}
+import org.apache.linkis.monitor.jobhistory.dao.JobHistoryMapper
+
+object MapperFactory {
+
+  private var jobHistoryMapper: JobHistoryMapper = _
+
+  private var instanceInfoMapper: InstanceInfoDao = _
+
+  private var instanceLabelMapper: InstanceLabelDao = _
+
+  private var instanceLabelRelationMapper: InsLabelRelationDao = _
+
+  def getJobHistoryMapper(): JobHistoryMapper = jobHistoryMapper
+
+  def setJobHistoryMapper(jobHistoryMapper: JobHistoryMapper): Unit = {
+    MapperFactory.jobHistoryMapper = jobHistoryMapper
+  }
+
+  def getInstanceInfoMapper(): InstanceInfoDao = instanceInfoMapper
+
+  def setInstanceInfoMapper(instanceInfoMapper: InstanceInfoDao): Unit = {
+    MapperFactory.instanceInfoMapper = instanceInfoMapper
+  }
+
+  def getInstanceLabelMapper(): InstanceLabelDao = instanceLabelMapper
+
+  def setInstanceLabelMapper(instanceLabelMapper: InstanceLabelDao): Unit = {
+    MapperFactory.instanceLabelMapper = instanceLabelMapper
+  }
+
+  def getInsLabelRelationMapper(): InsLabelRelationDao = instanceLabelRelationMapper
+
+  def setInsLabelRelationMapper(instanceLabelRelationMapper: InsLabelRelationDao): Unit = {
+    MapperFactory.instanceLabelRelationMapper = instanceLabelRelationMapper
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/JobHistoryDataFetcher.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/JobHistoryDataFetcher.scala
new file mode 100644
index 0000000..fb371a6
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/JobHistoryDataFetcher.scala
@@ -0,0 +1,117 @@
+/*
+ * 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.linkis.monitor.jobhistory
+
+import org.apache.linkis.common.utils.{Logging, Utils}
+import org.apache.linkis.monitor.constants.Constants
+import org.apache.linkis.monitor.core.pac.AbstractDataFetcher
+import org.apache.linkis.monitor.jobhistory.dao.JobHistoryMapper
+import org.apache.linkis.monitor.jobhistory.exception.AnomalyScannerException
+
+import org.apache.commons.lang3.StringUtils
+
+import java.util
+import java.util.Date
+
+class JobHistoryDataFetcher(args: Array[Any], mapper: JobHistoryMapper)
+    extends AbstractDataFetcher
+    with Logging {
+
+  /**
+   * retrieve JobHistory Data starts from startTimeMs and ends at startTimeMs + intervalsMs
+   *
+   * @return
+   */
+  /**
+   * get arguments for querying data
+   *
+   * @return
+   */
+  override def getArgs(): Array[Any] = args
+
+  /**
+   *   1. get Data given some arguments
+   */
+  override def getData(): util.List[scala.Any] = {
+    if (!args.isInstanceOf[Array[String]]) {
+      throw new AnomalyScannerException(
+        21304,
+        "Wrong input for JobHistoryDataFetcher. DataType: " + args.getClass.getCanonicalName
+      )
+    }
+    if (args != null && args.length == 2) {
+      val start = Utils.tryCatch(args(0).asInstanceOf[String].toLong) { t =>
+        {
+          logger.error("Failed to get data from DB: Illegal arguments.", t)
+          throw t
+        }
+      }
+      val end = Utils.tryCatch(args(1).asInstanceOf[String].toLong) { t =>
+        {
+          logger.error("Failed to get data from DB: Illegal arguments.", t)
+          throw t
+        }
+      }
+      mapper
+        .search(null, null, null, new Date(start), new Date(end), null)
+        .asInstanceOf[util.List[scala.Any]]
+    } else if (args != null && args.length == 4) {
+      val start = Utils.tryCatch(args(0).asInstanceOf[String].toLong) { t =>
+        {
+          logger.error("Failed to get data from DB: Illegal arguments.", t)
+          throw t
+        }
+      }
+      val end = Utils.tryCatch(args(1).asInstanceOf[String].toLong) { t =>
+        {
+          logger.error("Failed to get data from DB: Illegal arguments.", t)
+          throw t
+        }
+      }
+      val id = Utils.tryCatch(args(2).asInstanceOf[String].toLong) { t =>
+        {
+          logger.error("Failed to get data from DB: Illegal arguments.", t)
+          throw t
+        }
+      }
+      if (
+          StringUtils.isNotBlank(args(3).asInstanceOf[String]) && args(3)
+            .asInstanceOf[String]
+            .equals("updated_time")
+      ) {
+        val list = new util.ArrayList[String]()
+        Constants.DATA_FINISHED_JOB_STATUS_ARRAY.foreach(list.add)
+        mapper
+          .searchByCacheAndUpdateTime(id, null, list, new Date(start), new Date(end), null)
+          .asInstanceOf[util.List[scala.Any]]
+      } else {
+        val list = new util.ArrayList[String]()
+        Constants.DATA_UNFINISHED_JOB_STATUS_ARRAY.foreach(list.add)
+        mapper
+          .searchByCache(id, null, list, new Date(start), new Date(end), null)
+          .asInstanceOf[util.List[scala.Any]]
+      }
+    } else {
+      throw new AnomalyScannerException(
+        21304,
+        "Wrong input for JobHistoryDataFetcher. Data: " + args
+      )
+    }
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/errorcode/JobHistoryErrCodeHitEvent.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/errorcode/JobHistoryErrCodeHitEvent.scala
new file mode 100644
index 0000000..4e36d44
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/errorcode/JobHistoryErrCodeHitEvent.scala
@@ -0,0 +1,22 @@
+/*
+ * 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.linkis.monitor.jobhistory.errorcode
+
+import org.apache.linkis.monitor.core.ob.SingleObserverEvent
+
+class JobHistoryErrCodeHitEvent extends SingleObserverEvent
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/errorcode/JobHistoryErrCodeRule.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/errorcode/JobHistoryErrCodeRule.scala
new file mode 100644
index 0000000..d354f7f
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/errorcode/JobHistoryErrCodeRule.scala
@@ -0,0 +1,84 @@
+/*
+ * 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.linkis.monitor.jobhistory.errorcode
+
+import org.apache.linkis.common.utils.Logging
+import org.apache.linkis.monitor.core.ob.Observer
+import org.apache.linkis.monitor.core.pac.{AbstractScanRule, ScannedData}
+import org.apache.linkis.monitor.jobhistory.entity.JobHistory
+import org.apache.linkis.monitor.until.CacheUtils
+
+import java.util
+
+import scala.collection.JavaConverters._
+
+/**
+ * Monitor the error codes returned by executing tasks. When executing script tasks, the executed
+ * error codes will be recorded in the database. The service will generate an alarm based on the
+ * error code recorded in the database. If the error code contains (11001, 11002), the alarm will be
+ * triggered.
+ */
+class JobHistoryErrCodeRule(errorCodes: util.Set[String], hitObserver: Observer)
+    extends AbstractScanRule(event = new JobHistoryErrCodeHitEvent, observer = hitObserver)
+    with Logging {
+  private val scanRuleList = CacheUtils.cacheBuilder
+
+  /**
+   * if data match the pattern, return true and trigger observer should call isMatched()
+   *
+   * @param data
+   * @return
+   */
+  override def triggerIfMatched(data: util.List[ScannedData]): Boolean = {
+
+    if (!getHitEvent().isRegistered || null == data) {
+      logger.error("ScanRule is not bind with an observer. Will not be triggered")
+      return false
+    }
+
+    val alertData: util.List[JobHistory] = new util.ArrayList[JobHistory]()
+    for (sd <- data.asScala) {
+      if (sd != null && sd.getData() != null) {
+        for (d <- sd.getData().asScala) {
+          d match {
+            case history: JobHistory =>
+              if (errorCodes.contains(String.valueOf(history.getErrorCode))) {
+                alertData.add(history)
+              }
+              scanRuleList.put("jobHistoryId", history.getId)
+            case _ =>
+              logger.warn(
+                "Ignored wrong input data Type : " + d + ", " + d.getClass.getCanonicalName
+              )
+          }
+        }
+      } else {
+        logger.warn("Ignored null scanned data")
+      }
+
+    }
+    logger.info("hit " + alertData.size() + " data in one iteration")
+    if (alertData.size() > 0) {
+      getHitEvent().notifyObserver(getHitEvent(), alertData)
+      true
+    } else {
+      false
+    }
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/errorcode/JobHistoryErrorCodeAlertSender.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/errorcode/JobHistoryErrorCodeAlertSender.scala
new file mode 100644
index 0000000..7f3d8e1
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/errorcode/JobHistoryErrorCodeAlertSender.scala
@@ -0,0 +1,96 @@
+/*
+ * 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.linkis.monitor.jobhistory.errorcode
+
+import org.apache.linkis.common.utils.Logging
+import org.apache.linkis.monitor.core.ob.{Event, Observer}
+import org.apache.linkis.monitor.jobhistory.entity.JobHistory
+import org.apache.linkis.monitor.jobhistory.exception.AnomalyScannerException
+import org.apache.linkis.monitor.utils.alert.AlertDesc
+import org.apache.linkis.monitor.utils.alert.ims.{ImsAlertDesc, PooledImsAlertUtils}
+
+import java.util
+
+import scala.collection.JavaConverters._
+
+class JobHistoryErrorCodeAlertSender(alerts: util.Map[String, AlertDesc])
+    extends Observer
+    with Logging {
+
+  override def update(e: Event, jobHistoryList: scala.Any): Unit = {
+    if (!e.isInstanceOf[JobHistoryErrCodeHitEvent]) {
+      throw new AnomalyScannerException(
+        21304,
+        "Wrong event that triggers JobHistoryErrorCodeAlertSender. Input DataType: " + e.getClass.getCanonicalName
+      )
+    }
+    if (null == jobHistoryList || !jobHistoryList.isInstanceOf[util.List[_]]) {
+      throw new AnomalyScannerException(
+        21304,
+        "Wrong input for JobHistoryErrorCodeAlertSender. Input DataType: " + jobHistoryList.getClass.getCanonicalName
+      )
+    }
+    val toSend = new util.HashMap[String, ImsAlertDesc]
+    for (a <- jobHistoryList.asInstanceOf[util.List[_]].asScala) {
+      if (a == null) {
+        logger.warn("Ignore null input data")
+      } else if (!a.isInstanceOf[JobHistory]) {
+        logger.warn("Ignore wrong input data Type : " + a.getClass.getCanonicalName)
+      } else {
+        val jobHistory = a.asInstanceOf[JobHistory]
+        val errorCode = String.valueOf(jobHistory.getErrorCode)
+        if (alerts.containsKey(errorCode) && alerts.get(errorCode).isInstanceOf[ImsAlertDesc]) {
+          val alert = if (!toSend.containsKey(errorCode)) {
+            alerts.get(errorCode).asInstanceOf[ImsAlertDesc]
+          } else {
+            toSend.get(errorCode)
+          }
+
+          var newInfo = if (!toSend.containsKey(errorCode)) {
+            alert.alertInfo + "\n" +
+              "[error_code] " + jobHistory.getErrorCode + ", " + jobHistory.getErrorDesc + "\n"
+          } else {
+            alert.alertInfo
+          }
+          newInfo = newInfo +
+            "[job-info] " +
+            "submit-user: " + jobHistory.getSubmitUser + ", " +
+            "execute-user: " + jobHistory.getExecuteUser + ", " +
+            "engine_type: " + jobHistory.getEngineType + ", " +
+            "create_time: " + jobHistory.getCreatedTime + ", " +
+            "instance: " + jobHistory.getInstances + ". \n"
+          val newNumHit = alert.numHit + 1
+          toSend.put(errorCode, alert.copy(alertInfo = newInfo, numHit = newNumHit))
+        } else if (!alerts.containsKey(errorCode)) {
+          logger.warn("Ignored unregistered error code: " + errorCode)
+        } else if (!alerts.get(errorCode).isInstanceOf[ImsAlertDesc]) {
+          logger.warn(
+            "Ignored invalid alertDesc. DataType: " + alerts
+              .get(errorCode)
+              .getClass
+              .getCanonicalName
+          )
+        }
+      }
+    }
+    for ((_, alert) <- toSend.asScala) {
+      PooledImsAlertUtils.addAlert(alert)
+    }
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/jobtime/JobTimeExceedAlertSender.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/jobtime/JobTimeExceedAlertSender.scala
new file mode 100644
index 0000000..0a53142
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/jobtime/JobTimeExceedAlertSender.scala
@@ -0,0 +1,111 @@
+/*
+ * 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.linkis.monitor.jobhistory.jobtime
+
+import org.apache.linkis.common.utils.{Logging, Utils}
+import org.apache.linkis.monitor.config.MonitorConfig
+import org.apache.linkis.monitor.core.ob.{Event, Observer}
+import org.apache.linkis.monitor.jobhistory.entity.JobHistory
+import org.apache.linkis.monitor.jobhistory.exception.AnomalyScannerException
+import org.apache.linkis.monitor.utils.alert.AlertDesc
+import org.apache.linkis.monitor.utils.alert.ims.{ImsAlertDesc, PooledImsAlertUtils}
+
+import java.text.MessageFormat
+import java.util
+
+import scala.collection.JavaConverters._
+import scala.collection.mutable.ArrayBuffer
+
+class JobTimeExceedAlertSender(alerts: util.Map[String, AlertDesc]) extends Observer with Logging {
+
+  private val orderedThresholds: Array[Long] = {
+    val ret = new ArrayBuffer[Long]()
+    if (alerts != null) {
+      for (k <- alerts.keySet().asScala) {
+        Utils.tryCatch(ret.append(k.toLong)) { t =>
+          logger.warn("Ignored illegal threshold: " + k, t)
+          false
+        }
+      }
+    }
+    ret.toArray
+  }
+
+  override def update(e: Event, jobHistoryList: scala.Any): Unit = {
+    if (!e.isInstanceOf[JobTimeExceedHitEvent]) {
+      throw new AnomalyScannerException(
+        21304,
+        "Wrong event that triggers JobTimeExceedAlertSender. Input DataType: " + e.getClass.getCanonicalName
+      )
+    }
+    if (null == jobHistoryList || !jobHistoryList.isInstanceOf[util.List[_]]) {
+      throw new AnomalyScannerException(
+        21304,
+        "Wrong input for JobTimeExceedAlertSender. Input DataType: " + jobHistoryList.getClass.getCanonicalName
+      )
+    }
+    if (orderedThresholds.length == 0) {
+      logger.warn("Found none legal threshold, will not send any alert: " + this)
+      return
+    }
+    val toSend = new util.HashMap[String, ImsAlertDesc]
+    for (a <- jobHistoryList.asInstanceOf[util.List[_]].asScala) {
+      if (a == null) {
+        logger.warn("Ignore null input data")
+      } else if (!a.isInstanceOf[JobHistory]) {
+        logger.warn("Ignore wrong input data Type : " + a.getClass.getCanonicalName)
+      } else {
+        val jobHistory = a.asInstanceOf[JobHistory]
+        val elapse = System.currentTimeMillis() - jobHistory.getCreatedTime.getTime
+        var ts = 0L
+        for (t <- orderedThresholds) { // search max threshold that is smaller than elapse
+          if (elapse >= t) {
+            ts = t
+          } else {}
+        }
+        val name = ts.toString
+        val alert = if (!toSend.containsKey(name)) {
+          alerts
+            .get(name)
+            .asInstanceOf[ImsAlertDesc]
+        } else {
+          toSend.get(name)
+        }
+
+        val newInfo = MessageFormat.format(
+          MonitorConfig.TASK_RUNTIME_TIMEOUT_DESC.getValue,
+          jobHistory.getId,
+          (elapse / 1000 / 60 / 60).toString,
+          jobHistory.getInstances,
+          MonitorConfig.SOLUTION_URL.getValue
+        )
+
+        val newNumHit = alert.numHit + 1
+        val receiver = new util.HashSet[String]()
+        receiver.add(jobHistory.getSubmitUser)
+        receiver.add(jobHistory.getExecuteUser)
+        receiver.addAll(alert.alertReceivers)
+        val ImsAlertDesc =
+          alert.copy(alertInfo = newInfo, alertReceivers = receiver, numHit = newNumHit)
+        PooledImsAlertUtils.addAlert(ImsAlertDesc)
+
+      }
+    }
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/jobtime/JobTimeExceedHitEvent.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/jobtime/JobTimeExceedHitEvent.scala
new file mode 100644
index 0000000..96c0b42
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/jobtime/JobTimeExceedHitEvent.scala
@@ -0,0 +1,22 @@
+/*
+ * 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.linkis.monitor.jobhistory.jobtime
+
+import org.apache.linkis.monitor.core.ob.SingleObserverEvent
+
+class JobTimeExceedHitEvent extends SingleObserverEvent
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/jobtime/JobTimeExceedRule.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/jobtime/JobTimeExceedRule.scala
new file mode 100644
index 0000000..f788173
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/jobtime/JobTimeExceedRule.scala
@@ -0,0 +1,104 @@
+/*
+ * 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.linkis.monitor.jobhistory.jobtime
+
+import org.apache.linkis.common.utils.Logging
+import org.apache.linkis.monitor.constants.Constants
+import org.apache.linkis.monitor.core.ob.Observer
+import org.apache.linkis.monitor.core.pac.{AbstractScanRule, ScannedData}
+import org.apache.linkis.monitor.jobhistory.entity.JobHistory
+import org.apache.linkis.monitor.jobhistory.exception.AnomalyScannerException
+import org.apache.linkis.monitor.until.CacheUtils
+
+import java.util
+import java.util.Locale
+
+import scala.collection.JavaConverters._
+
+/**
+ * Monitor the execution status of tasks, scan data outside 12 hours and within 24 hours, If within
+ * the scope of the rule, there is data whose status is one of (Inited, WaitForRetry, Scheduled,
+ * Running), an alarm will be triggered.
+ */
+class JobTimeExceedRule(thresholds: util.Set[String], hitObserver: Observer)
+    extends AbstractScanRule(event = new JobTimeExceedHitEvent, observer = hitObserver)
+    with Logging {
+
+  private val threshold: Long = {
+    if (thresholds == null) {
+      throw new AnomalyScannerException(21304, "thresholds should not be null")
+    }
+    var t = Long.MaxValue
+    for (k <- thresholds.asScala) {
+      if (k != null) {
+        if (t > k.toLong) {
+          t = k.toLong
+        }
+      } else {
+        logger.warn("ignored null input")
+      }
+    }
+    t
+  }
+
+  private val scanRuleList = CacheUtils.cacheBuilder
+
+  /**
+   * if data match the pattern, return true and trigger observer should call isMatched()
+   *
+   * @param data
+   * @return
+   */
+  override def triggerIfMatched(data: util.List[ScannedData]): Boolean = {
+    if (!getHitEvent.isRegistered || data == null) {
+      logger.error("ScanRule is not bind with an observer. Will not be triggered")
+      return false
+    }
+    val alertData: util.List[JobHistory] = new util.ArrayList[JobHistory]()
+    for (sd <- data.asScala) {
+      if (sd != null && sd.getData() != null) {
+        for (d <- sd.getData().asScala) {
+          if (d.isInstanceOf[JobHistory]) {
+            val jobHistory = d.asInstanceOf[JobHistory]
+            val status = jobHistory.getStatus.toUpperCase(Locale.getDefault)
+            if (Constants.UNFINISHED_JOB_STATUS.contains(status)) {
+              val elapse = System.currentTimeMillis() - jobHistory.getCreatedTime.getTime
+              if (elapse / 1000 >= threshold) {
+                alertData.add(d.asInstanceOf[JobHistory])
+              }
+            }
+            scanRuleList.put("jobhistoryScan", jobHistory.getId)
+          } else {
+            logger.warn("Ignored wrong input data Type : " + d + ", " + d.getClass.getCanonicalName)
+          }
+        }
+      } else {
+        logger.warn("Ignored null scanned data")
+      }
+
+    }
+    logger.info("hit " + alertData.size() + " data in one iteration")
+    if (alertData.size() > 0) {
+      getHitEvent.notifyObserver(getHitEvent, alertData)
+      true
+    } else {
+      false
+    }
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/labels/JobHistoryLabelsAlertSender.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/labels/JobHistoryLabelsAlertSender.scala
new file mode 100644
index 0000000..7876156
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/labels/JobHistoryLabelsAlertSender.scala
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor.jobhistory.labels
+
+import org.apache.linkis.common.utils.Logging
+import org.apache.linkis.monitor.constants.Constants
+import org.apache.linkis.monitor.core.ob.{Event, Observer}
+import org.apache.linkis.monitor.jobhistory.entity.JobHistory
+import org.apache.linkis.monitor.jobhistory.exception.AnomalyScannerException
+import org.apache.linkis.monitor.utils.alert.AlertDesc
+import org.apache.linkis.monitor.utils.alert.ims.{PooledImsAlertUtils, UserLabelAlertUtils}
+import org.apache.linkis.server.BDPJettyServerHelper
+
+import java.util
+
+import scala.collection.JavaConverters._
+import scala.collection.mutable.ArrayBuffer
+
+class JobHistoryLabelsAlertSender() extends Observer with Logging {
+
+  override def update(e: Event, jobHistoryList: scala.Any): Unit = {
+    if (!e.isInstanceOf[JobHistoryLabelsHitEvent]) {
+      throw new AnomalyScannerException(
+        21304,
+        "Wrong event that triggers JobHistoryLabelsAlertSender. Input DataType: " + e.getClass.getCanonicalName
+      )
+    }
+    if (null == jobHistoryList || !jobHistoryList.isInstanceOf[util.List[_]]) {
+      throw new AnomalyScannerException(
+        21304,
+        "Wrong input for JobHistoryLabelsAlertSender. Input DataType: " + jobHistoryList.getClass.getCanonicalName
+      )
+    }
+    val toSend = new ArrayBuffer[String]
+    for (a <- jobHistoryList.asInstanceOf[util.List[_]].asScala) {
+      if (a == null) {
+        logger.warn("Ignore null input data")
+      } else if (!a.isInstanceOf[JobHistory]) {
+        logger.warn("Ignore wrong input data Type : " + a.getClass.getCanonicalName)
+      } else {
+        val jobHistory = a.asInstanceOf[JobHistory]
+        toSend.append(jobHistory.getLabels)
+      }
+    }
+    for (str <- toSend.distinct) {
+      val labelsMap: util.Map[String, String] =
+        BDPJettyServerHelper.gson.fromJson(str, classOf[java.util.Map[String, String]])
+      val alerts: util.Map[String, AlertDesc] =
+        UserLabelAlertUtils.getAlerts(Constants.USER_LABEL_MONITOR, labelsMap.get("userCreator"))
+      PooledImsAlertUtils.addAlert(alerts.get("12010"));
+    }
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/labels/JobHistoryLabelsHitEvent.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/labels/JobHistoryLabelsHitEvent.scala
new file mode 100644
index 0000000..d51c3c4
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/labels/JobHistoryLabelsHitEvent.scala
@@ -0,0 +1,22 @@
+/*
+ * 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.linkis.monitor.jobhistory.labels
+
+import org.apache.linkis.monitor.core.ob.SingleObserverEvent
+
+class JobHistoryLabelsHitEvent extends SingleObserverEvent
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/labels/JobHistoryLabelsRule.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/labels/JobHistoryLabelsRule.scala
new file mode 100644
index 0000000..70d309b
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/labels/JobHistoryLabelsRule.scala
@@ -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.linkis.monitor.jobhistory.labels
+
+import org.apache.linkis.common.utils.Logging
+import org.apache.linkis.monitor.constants.Constants
+import org.apache.linkis.monitor.core.ob.Observer
+import org.apache.linkis.monitor.core.pac.{AbstractScanRule, ScannedData}
+import org.apache.linkis.monitor.jobhistory.entity.JobHistory
+import org.apache.linkis.monitor.until.CacheUtils
+import org.apache.linkis.server.BDPJettyServerHelper
+
+import org.apache.commons.lang3.StringUtils
+
+import java.util
+
+import scala.collection.JavaConverters._
+
+import com.google.common.collect.HashBiMap
+
+/**
+ * Scan the execution data within the previous 20 minutes and judge the labels field of the data.
+ * Judgment based on monitor configuration (linkis.monitor.jobhistory.userLabel.tenant)
+ */
+class JobHistoryLabelsRule(hitObserver: Observer)
+    extends AbstractScanRule(event = new JobHistoryLabelsHitEvent, observer = hitObserver)
+    with Logging {
+
+  private val scanRuleList = CacheUtils.cacheBuilder
+
+  /**
+   * if data match the pattern, return true and trigger observer should call isMatched()
+   *
+   * @param data
+   * @return
+   */
+  override def triggerIfMatched(data: util.List[ScannedData]): Boolean = {
+    if (!getHitEvent.isRegistered || null == data) {
+      logger.error("ScanRule is not bind with an observer. Will not be triggered")
+      return false
+    }
+    val alertData: util.List[JobHistory] = new util.ArrayList[JobHistory]()
+    for (sd <- data.asScala) {
+      if (sd != null && sd.getData() != null) {
+        for (d <- sd.getData().asScala) {
+          if (d.isInstanceOf[JobHistory]) {
+            logger.info(" start jobhistory user label rule data : {}", d)
+            val jobHistory = d.asInstanceOf[JobHistory]
+            val labels = jobHistory.getLabels
+            val labelsMap: util.Map[String, String] =
+              BDPJettyServerHelper.gson.fromJson(labels, classOf[java.util.Map[String, String]])
+            val userCreator = labelsMap.get("userCreator");
+            val tenant = labelsMap.get("tenant");
+            if (StringUtils.isNotBlank(userCreator)) {
+              val configMap = BDPJettyServerHelper.gson.fromJson(
+                Constants.USER_LABEL_TENANT.getValue,
+                classOf[java.util.Map[String, String]]
+              )
+              val listIterator = configMap.keySet.iterator
+              while ({
+                listIterator.hasNext
+              }) {
+                val next = listIterator.next
+                if (userCreator.contains(next)) {
+                  val value = configMap.get(next)
+                  if (!value.equals(tenant)) {
+                    alertData.add(d.asInstanceOf[JobHistory])
+                  }
+                }
+              }
+              if (configMap.values().contains(tenant)) {
+                val bimap: HashBiMap[String, String] = HashBiMap.create(configMap)
+                val key = bimap.inverse().get(tenant)
+                if (!key.contains(userCreator)) {
+                  alertData.add(d.asInstanceOf[JobHistory])
+                }
+              }
+            }
+            scanRuleList.put("jobHistoryId", jobHistory.getId)
+          } else {
+            logger.warn("Ignored wrong input data Type : " + d + ", " + d.getClass.getCanonicalName)
+          }
+        }
+      } else {
+        logger.warn("Ignored null scanned data")
+      }
+    }
+    logger.info("hit " + alertData.size() + " data in one iteration")
+    if (alertData.size() > 0) {
+      getHitEvent.notifyObserver(getHitEvent, alertData)
+      true
+    } else {
+      false
+    }
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/CommonJobRunTimeRule.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/CommonJobRunTimeRule.scala
new file mode 100644
index 0000000..77d904f
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/CommonJobRunTimeRule.scala
@@ -0,0 +1,83 @@
+/*
+ * 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.linkis.monitor.jobhistory.runtime
+
+import org.apache.linkis.common.utils.Logging
+import org.apache.linkis.monitor.constants.Constants
+import org.apache.linkis.monitor.core.ob.Observer
+import org.apache.linkis.monitor.core.pac.{AbstractScanRule, ScannedData}
+import org.apache.linkis.monitor.jobhistory.entity.JobHistory
+
+import org.apache.commons.lang3.StringUtils
+
+import java.util
+
+import scala.collection.JavaConverters._
+
+/**
+ * Scan the execution data within the first 20 minutes,
+ *   1. The ObserveInfo field of the data is judged whether it is empty, 2. The task status has been
+ *      completed (Succeed, Failed, Cancelled, Timeout, ALL) Alarms can be triggered when conditions
+ *      are met
+ */
+class CommonJobRunTimeRule(hitObserver: Observer)
+    extends AbstractScanRule(event = new JobHistoryRunTimeHitEvent, observer = hitObserver)
+    with Logging {
+
+  /**
+   * if data match the pattern, return true and trigger observer should call isMatched()
+   *
+   * @param data
+   * @return
+   */
+  override def triggerIfMatched(data: util.List[ScannedData]): Boolean = {
+    if (!getHitEvent.isRegistered || null == data) {
+      logger.error("ScanRule is not bind with an observer. Will not be triggered")
+      return false
+    }
+    val alertData: util.List[JobHistory] = new util.ArrayList[JobHistory]()
+    for (sd <- data.asScala) {
+      if (sd != null && sd.getData() != null) {
+        for (d <- sd.getData().asScala) {
+          d match {
+            case jobHistory: JobHistory =>
+              if (
+                  Constants.FINISHED_JOB_STATUS.contains(jobHistory.getStatus.toUpperCase())
+                  && StringUtils.isNotBlank(jobHistory.getObserveInfo)
+              ) {
+                alertData.add(jobHistory)
+              } else {
+                logger.warn("jobHistory is not completely  , taskid :" + d)
+              }
+            case _ =>
+          }
+        }
+      } else {
+        logger.warn("Ignored null scanned data")
+      }
+    }
+    logger.info("hit " + alertData.size() + " data in one iteration")
+    if (alertData.size() > 0) {
+      getHitEvent.notifyObserver(getHitEvent, alertData)
+      true
+    } else {
+      false
+    }
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/CommonRunTimeAlertSender.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/CommonRunTimeAlertSender.scala
new file mode 100644
index 0000000..2380891
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/CommonRunTimeAlertSender.scala
@@ -0,0 +1,98 @@
+/*
+ * 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.linkis.monitor.jobhistory.runtime
+
+import org.apache.linkis.common.utils.Logging
+import org.apache.linkis.monitor.constants.Constants
+import org.apache.linkis.monitor.core.ob.{Event, Observer}
+import org.apache.linkis.monitor.jobhistory.entity.JobHistory
+import org.apache.linkis.monitor.jobhistory.exception.AnomalyScannerException
+import org.apache.linkis.monitor.utils.alert.ims.{MonitorAlertUtils, PooledImsAlertUtils}
+import org.apache.linkis.server.BDPJettyServerHelper
+
+import org.apache.commons.collections.MapUtils
+
+import java.net.InetAddress
+import java.text.SimpleDateFormat
+import java.util
+import java.util.Date
+
+import scala.collection.JavaConverters._
+
+class CommonRunTimeAlertSender() extends Observer with Logging {
+  private val dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
+
+  override def update(e: Event, jobHistoryList: scala.Any): Unit = {
+    if (!e.isInstanceOf[JobHistoryRunTimeHitEvent]) {
+      throw new AnomalyScannerException(
+        21304,
+        "Wrong event that triggers JobHistoryErrorCodeAlertSender. Input DataType: " + e.getClass.getCanonicalName
+      )
+    }
+    if (!jobHistoryList.isInstanceOf[util.List[_]] || null == jobHistoryList) {
+      throw new AnomalyScannerException(
+        21304,
+        "Wrong input for JobHistoryErrorCodeAlertSender. Input DataType: " + jobHistoryList.getClass.getCanonicalName
+      )
+    }
+    for (a <- jobHistoryList.asInstanceOf[util.List[_]].asScala) {
+      if (a == null) {
+        logger.warn("Ignore null input data")
+      } else if (!a.isInstanceOf[JobHistory]) {
+        logger.warn("Ignore wrong input data Type : " + a.getClass.getCanonicalName)
+      } else {
+        val jobHistory = a.asInstanceOf[JobHistory]
+        val observeInfoMap = BDPJettyServerHelper.gson.fromJson(
+          jobHistory.getObserveInfo,
+          classOf[java.util.Map[String, String]]
+        )
+        val extraMap = MapUtils.getMap(observeInfoMap, "extra")
+        observeInfoMap.put(
+          "title",
+          extraMap
+            .get("title")
+            .toString + ",任务id:" + jobHistory.getId + ",执行结果 :" + jobHistory.getStatus
+        )
+        observeInfoMap.put(
+          "$detail",
+          extraMap.get("detail").toString + ",执行结果 :" + jobHistory.getStatus
+        )
+        observeInfoMap.put("$submitUser", jobHistory.getSubmitUser)
+        observeInfoMap.put("$status", jobHistory.getStatus)
+        observeInfoMap.put("$id", jobHistory.getId.toString)
+        observeInfoMap.put("$date", dateFormat.format(new Date()))
+        var alterSysInfo = ""
+        if (null != extraMap.get("alterSysInfo")) {
+          alterSysInfo = extraMap.get("alterSysInfo").toString
+        }
+        observeInfoMap.put("$sysid", alterSysInfo)
+        var alterObject = ""
+        if (null != extraMap.get("alterObject")) {
+          alterObject = extraMap.get("alterObject").toString
+        }
+        observeInfoMap.put("$object", alterObject)
+        observeInfoMap.put("$ip", InetAddress.getLocalHost.getHostAddress)
+        observeInfoMap.remove("taskId")
+        observeInfoMap.remove("extra")
+        val alters = MonitorAlertUtils.getAlerts(Constants.JOB_RESULT_IM, observeInfoMap)
+        PooledImsAlertUtils.addAlert(alters.get("12016"))
+      }
+    }
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/CommonRunTimeHitEvent.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/CommonRunTimeHitEvent.scala
new file mode 100644
index 0000000..8b2f6d4
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/CommonRunTimeHitEvent.scala
@@ -0,0 +1,22 @@
+/*
+ * 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.linkis.monitor.jobhistory.runtime
+
+import org.apache.linkis.monitor.core.ob.SingleObserverEvent
+
+class CommonRunTimeHitEvent extends SingleObserverEvent
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/JobHistoryRunTimeAlertSender.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/JobHistoryRunTimeAlertSender.scala
new file mode 100644
index 0000000..a1e870c
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/JobHistoryRunTimeAlertSender.scala
@@ -0,0 +1,72 @@
+/*
+ * 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.linkis.monitor.jobhistory.runtime
+
+import org.apache.linkis.common.utils.Logging
+import org.apache.linkis.monitor.constants.Constants
+import org.apache.linkis.monitor.core.ob.{Event, Observer}
+import org.apache.linkis.monitor.jobhistory.entity.JobHistory
+import org.apache.linkis.monitor.jobhistory.exception.AnomalyScannerException
+import org.apache.linkis.monitor.utils.alert.ims.{MonitorAlertUtils, PooledImsAlertUtils}
+
+import java.util
+
+import scala.collection.JavaConverters._
+
+/**
+ * Scan the execution data within the first 20 minutes, judge the completed tasks,
+ *   1. The parm field in jobhistory contains (task.notification.conditions) 2. If the result of
+ *      executing the task is any one of (Succeed, Failed, Canceled, Timeout, ALL), an alarm will be
+ *      triggered 3.The result of the job is that it has ended The alarm can be triggered if the
+ *      above three conditions are met at the same time
+ */
+class JobHistoryRunTimeAlertSender() extends Observer with Logging {
+
+  override def update(e: Event, jobHistroyList: scala.Any): Unit = {
+    if (!e.isInstanceOf[JobHistoryRunTimeHitEvent]) {
+      throw new AnomalyScannerException(
+        21304,
+        "Wrong event that triggers JobHistoryErrorCodeAlertSender. Input DataType: " + e.getClass.getCanonicalName
+      )
+    }
+    if (null == jobHistroyList || !jobHistroyList.isInstanceOf[util.List[_]]) {
+      throw new AnomalyScannerException(
+        21304,
+        "Wrong input for JobHistoryErrorCodeAlertSender. Input DataType: " + jobHistroyList.getClass.getCanonicalName
+      )
+    }
+    for (a <- jobHistroyList.asInstanceOf[util.List[_]].asScala) {
+      if (a == null) {
+        logger.warn("Ignore null input data")
+      } else if (!a.isInstanceOf[JobHistory]) {
+        logger.warn("Ignore wrong input data Type : " + a.getClass.getCanonicalName)
+      } else {
+        // 您的任务ID 1234 执行完成,最终状态为:成功、失败、取消
+        val jobHistory = a.asInstanceOf[JobHistory]
+        val status = jobHistory.getStatus
+        val replaceParm: util.HashMap[String, String] = new util.HashMap[String, String]
+        replaceParm.put("$id", String.valueOf(jobHistory.getId))
+        replaceParm.put("$status", status)
+        replaceParm.put("$alteruser", jobHistory.getSubmitUser)
+        val alters = MonitorAlertUtils.getAlerts(Constants.JOB_RESULT_IM, replaceParm)
+        PooledImsAlertUtils.addAlert(alters.get("12015"))
+      }
+    }
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/JobHistoryRunTimeHitEvent.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/JobHistoryRunTimeHitEvent.scala
new file mode 100644
index 0000000..9daaf02
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/JobHistoryRunTimeHitEvent.scala
@@ -0,0 +1,22 @@
+/*
+ * 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.linkis.monitor.jobhistory.runtime
+
+import org.apache.linkis.monitor.core.ob.SingleObserverEvent
+
+class JobHistoryRunTimeHitEvent extends SingleObserverEvent
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/JobHistoryRunTimeRule.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/JobHistoryRunTimeRule.scala
new file mode 100644
index 0000000..d350bc3
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/jobhistory/runtime/JobHistoryRunTimeRule.scala
@@ -0,0 +1,91 @@
+/*
+ * 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.linkis.monitor.jobhistory.runtime
+
+import org.apache.linkis.common.utils.Logging
+import org.apache.linkis.monitor.constants.Constants
+import org.apache.linkis.monitor.core.ob.Observer
+import org.apache.linkis.monitor.core.pac.{AbstractScanRule, ScannedData}
+import org.apache.linkis.monitor.jobhistory.entity.JobHistory
+import org.apache.linkis.monitor.until.CacheUtils
+import org.apache.linkis.protocol.utils.TaskUtils
+import org.apache.linkis.server.BDPJettyServerHelper
+
+import java.util
+
+import scala.collection.JavaConverters._
+
+class JobHistoryRunTimeRule(hitObserver: Observer)
+    extends AbstractScanRule(event = new JobHistoryRunTimeHitEvent, observer = hitObserver)
+    with Logging {
+  private val scanRuleList = CacheUtils.cacheBuilder
+
+  /**
+   * if data match the pattern, return true and trigger observer should call isMatched()
+   *
+   * @param data
+   * @return
+   */
+  override def triggerIfMatched(data: util.List[ScannedData]): Boolean = {
+    if (null == data || !getHitEvent.isRegistered) {
+      logger.error("ScanRule is not bind with an observer. Will not be triggered")
+      return false
+    }
+    val alertData: util.List[JobHistory] = new util.ArrayList[JobHistory]()
+    for (sd <- data.asScala) {
+      if (sd != null && sd.getData() != null) {
+        for (d <- sd.getData().asScala) {
+          d match {
+            case jobHistory: JobHistory =>
+              if (Constants.FINISHED_JOB_STATUS.contains(jobHistory.getStatus.toUpperCase())) {
+                val parmsMap: util.Map[String, scala.AnyRef] = BDPJettyServerHelper.gson.fromJson(
+                  jobHistory.getParams,
+                  classOf[util.Map[String, scala.AnyRef]]
+                )
+                val runtimeMap = TaskUtils.getRuntimeMap(parmsMap)
+                if (
+                    runtimeMap.containsKey("task.notification.conditions") &&
+                    Constants.FINISHED_JOB_STATUS.contains(
+                      String.valueOf(runtimeMap.get("task.notification.conditions")).toUpperCase()
+                    )
+                ) {
+                  alertData.add(jobHistory)
+                }
+              } else {
+                logger.warn(
+                  "Ignored wrong input data Type : " + d + ", " + d.getClass.getCanonicalName
+                )
+              }
+              scanRuleList.put("jobHistoryId", jobHistory.getId)
+            case _ =>
+          }
+        }
+      } else {
+        logger.warn("Ignored null scanned data")
+      }
+    }
+    logger.info("hit " + alertData.size() + " data in one iteration")
+    if (alertData.size() > 0) {
+      getHitEvent.notifyObserver(getHitEvent, alertData)
+      true
+    } else {
+      false
+    }
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/request/EmsListAction.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/request/EmsListAction.scala
new file mode 100644
index 0000000..6f3158e
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/request/EmsListAction.scala
@@ -0,0 +1,74 @@
+/*
+ * 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.linkis.monitor.request
+
+import org.apache.linkis.httpclient.request.GetAction
+
+import org.apache.commons.lang3.StringUtils
+
+import scala.collection.mutable.ArrayBuffer
+
+class EmsListAction extends GetAction with MonitorResourceAction {
+
+  override def suffixURLs: Array[String] = Array("linkisManager", "listAllEMs")
+
+}
+
+object EmsListAction {
+  def newBuilder(): Builder = new Builder
+
+  class Builder private[EmsListAction] () {
+    private var user: String = _
+    private var instance: String = _
+    private var nodeHealthy: String = _
+    private var owner: String = _
+
+    def setInstance(instance: String): Builder = {
+      this.instance = instance
+      this
+    }
+
+    def setNodeHealthy(nodeHealthy: String): Builder = {
+      this.nodeHealthy = nodeHealthy
+      this
+    }
+
+    def setOwner(owner: String): Builder = {
+      this.owner = owner
+      this
+    }
+
+    def setUser(user: String): Builder = {
+      this.user = user
+      this
+    }
+
+    def build(): EmsListAction = {
+      val emsListAction = new EmsListAction
+      if (StringUtils.isNotBlank(instance)) emsListAction.setParameter("instance", instance)
+      if (StringUtils.isNotBlank(nodeHealthy)) {
+        emsListAction.setParameter("nodeHealthy", nodeHealthy)
+      }
+      if (StringUtils.isNotBlank(owner)) emsListAction.setParameter("owner", owner)
+      if (StringUtils.isNotBlank(user)) emsListAction.setUser(user)
+      emsListAction
+    }
+
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/request/EntranceTaskAction.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/request/EntranceTaskAction.scala
new file mode 100644
index 0000000..f3175d8
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/request/EntranceTaskAction.scala
@@ -0,0 +1,77 @@
+/*
+ * 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.linkis.monitor.request
+
+import org.apache.linkis.httpclient.request.GetAction
+
+import org.apache.commons.lang3.StringUtils
+
+class EntranceTaskAction extends GetAction with MonitorResourceAction {
+  override def suffixURLs: Array[String] = Array("entrance/operation/metrics", "taskinfo")
+}
+
+object EntranceTaskAction {
+  def newBuilder(): Builder = new Builder
+
+  class Builder private[EntranceTaskAction] () {
+    private var user: String = _
+    private var creator: String = _
+    private var engineTypeLabel: String = _
+    private var instance: String = _
+
+    def setCreator(creator: String): Builder = {
+      this.creator = creator
+      this
+    }
+
+    def setEngineTypeLabel(engineTypeLabel: String): Builder = {
+      this.engineTypeLabel = engineTypeLabel
+      this
+    }
+
+    def setUser(user: String): Builder = {
+      this.user = user
+      this
+    }
+
+    def setInstance(instance: String): Builder = {
+      this.instance = instance
+      this
+    }
+
+    def build(): EntranceTaskAction = {
+      val entranceTaskAction = new EntranceTaskAction
+      if (StringUtils.isNotBlank(creator)) entranceTaskAction.setParameter("creator", creator)
+      if (StringUtils.isNotBlank(engineTypeLabel))
+        entranceTaskAction.setParameter("engineTypeLabel", engineTypeLabel)
+      if (StringUtils.isNotBlank(instance)) entranceTaskAction.setParameter("instance", instance)
+      if (StringUtils.isNotBlank(user)) {
+        // hadoop用户应该获取全部用户entrance信息,则无需传user,即可获取全部entrance信息
+        if (user.equals("hadoop")) {
+          entranceTaskAction.setParameter("user", "")
+        } else {
+          entranceTaskAction.setParameter("user", user)
+        }
+      }
+      if (StringUtils.isNotBlank(user)) entranceTaskAction.setUser(user)
+      entranceTaskAction
+    }
+
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/request/MonitorResourceAction.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/request/MonitorResourceAction.scala
new file mode 100644
index 0000000..7ea2001
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/request/MonitorResourceAction.scala
@@ -0,0 +1,22 @@
+/*
+ * 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.linkis.monitor.request
+
+import org.apache.linkis.httpclient.dws.request.DWSHttpAction
+
+trait MonitorResourceAction extends DWSHttpAction with UserAction
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/request/UserAction.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/request/UserAction.scala
new file mode 100644
index 0000000..4733a1b
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/request/UserAction.scala
@@ -0,0 +1,26 @@
+/*
+ * 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.linkis.monitor.request
+
+trait UserAction extends org.apache.linkis.httpclient.request.UserAction {
+  private var user: String = _
+
+  override def setUser(user: String): Unit = this.user = user
+
+  override def getUser: String = user
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/response/EntranceTaskResult.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/response/EntranceTaskResult.scala
new file mode 100644
index 0000000..33c695c
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/response/EntranceTaskResult.scala
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor.response
+
+import org.apache.linkis.httpclient.dws.annotation.DWSHttpMessageResult
+import org.apache.linkis.httpclient.dws.response.DWSResult
+
+import java.util
+
+import scala.beans.BeanProperty
+
+@DWSHttpMessageResult("/api/rest_j/v\\d+/entrance/operation/metrics/taskinfo")
+class EntranceTaskResult extends DWSResult {
+
+  @BeanProperty
+  var tasks: util.ArrayList[util.Map[String, Object]] = _
+
+  @BeanProperty
+  var totalPage: Int = _
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/response/MonitorResourceResult.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/response/MonitorResourceResult.scala
new file mode 100644
index 0000000..1c12662
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/response/MonitorResourceResult.scala
@@ -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.
+ */
+
+package org.apache.linkis.monitor.response
+
+import org.apache.linkis.httpclient.dws.response.DWSResult
+import org.apache.linkis.httpclient.request.UserAction
+
+trait MonitorResourceResult extends DWSResult with UserAction {
+
+  private var execID: String = _
+
+  def getExecID: String = execID
+
+  def setExecID(execID: String): Unit = {
+    this.execID = execID
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/ScanUtils.java b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/ScanUtils.java
new file mode 100644
index 0000000..5c5566c
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/ScanUtils.java
@@ -0,0 +1,71 @@
+/*
+ * 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.linkis.monitor.utils;
+
+
+public class ScanUtils {
+    public static int getNumOfLines(String str) {
+        if (str == null || str.length() == 0) {
+            return 0;
+        }
+        int lines = 1;
+        int len = str.length();
+        for (int pos = 0; pos < len; pos++) {
+            char c = str.charAt(pos);
+            if (c == '\r') {
+                lines++;
+                if (pos + 1 < len && str.charAt(pos + 1) == '\n') {
+                    pos++;
+                }
+            } else if (c == '\n') {
+                lines++;
+            }
+        }
+        return lines;
+    }
+
+    public static int getFirstIndexSkippingLines(String str, Integer lines) {
+        if (str == null || str.length() == 0 || lines < 0) {
+            return -1;
+        }
+        if (lines == 0) {
+            return 0;
+        }
+
+        int curLineIdx = 0;
+        int len = str.length();
+        for (int pos = 0; pos < len; pos++) {
+            char c = str.charAt(pos);
+            if (c == '\r') {
+                curLineIdx++;
+                if (pos + 1 < len && str.charAt(pos + 1) == '\n') {
+                    pos++;
+                }
+            } else if (c == '\n') {
+                curLineIdx++;
+            } else {
+                continue;
+            }
+
+            if (curLineIdx >= lines) {
+                return pos + 1;
+            }
+        }
+        return -1;
+    }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/AlertDesc.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/AlertDesc.scala
new file mode 100644
index 0000000..8a3be38
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/AlertDesc.scala
@@ -0,0 +1,25 @@
+/*
+ * 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.linkis.monitor.utils.alert
+
+trait AlertDesc {
+
+  /**
+   * define necessary information for an alert e.g. alert title, alert receiver etc.
+   */
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/AlertSender.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/AlertSender.scala
new file mode 100644
index 0000000..68ec0f6
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/AlertSender.scala
@@ -0,0 +1,29 @@
+/*
+ * 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.linkis.monitor.utils.alert
+
+trait AlertSender {
+
+  /**
+   * traverse all registered alertActions and send alert
+   *
+   * @return
+   *   true if it is a success
+   */
+  def doSendAlert(alertAction: AlertDesc): Boolean
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/PooledAlertSender.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/PooledAlertSender.scala
new file mode 100644
index 0000000..6214b63
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/PooledAlertSender.scala
@@ -0,0 +1,111 @@
+/*
+ * 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.linkis.monitor.utils.alert
+
+import org.apache.linkis.common.conf.CommonVars
+import org.apache.linkis.common.utils.{Logging, Utils}
+
+import java.util.concurrent.{Future, LinkedBlockingQueue}
+import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger}
+
+abstract class PooledAlertSender extends AlertSender with Logging {
+  private val THREAD_POOL_SIZE = CommonVars[Int]("linkis.alert.pool.size", 5).getValue
+
+  private val alertDescQ: LinkedBlockingQueue[AlertDesc] =
+    new LinkedBlockingQueue[AlertDesc](1000)
+
+  protected implicit val executors =
+    Utils.newCachedExecutionContext(THREAD_POOL_SIZE, "alert-pool-thread-", false)
+
+  private val stopped: AtomicBoolean = new AtomicBoolean(false)
+  private val runningNumber: AtomicInteger = new AtomicInteger(0)
+  private var future: Future[_] = _
+
+  /**
+   * add an alertDesc to queue
+   *
+   * @param alertDesc
+   *   should encapsulates every information an alert platform needs for sending an alarm
+   */
+  def addAlertToPool(alertDesc: AlertDesc): Unit = {
+    alertDescQ.add(alertDesc)
+  }
+
+  /**
+   * describes actual actions for sending an alert
+   *
+   * @return
+   *   true if it is a success
+   */
+  override def doSendAlert(alertDesc: AlertDesc): Boolean
+
+  def start(): Unit = {
+    future = Utils.defaultScheduler.submit(new Runnable() {
+      override def run() {
+        logger.info("Pooled alert thread started!")
+        while (!stopped.get) {
+          executors synchronized {
+            while (!stopped.get && runningNumber.get >= THREAD_POOL_SIZE) {
+              logger.info("Pooled alert thread is full, start waiting")
+              executors.wait()
+            }
+          }
+          logger.info("Pooled alert thread continue processing")
+
+          if (stopped.get && alertDescQ.size() == 0) return
+          val alertDesc = Utils.tryQuietly(alertDescQ.take)
+          if (alertDesc == null) return
+          executors.submit(new Runnable {
+            override def run() {
+              runningNumber.addAndGet(1)
+              Utils.tryAndWarn {
+                logger.info("sending alert , information: " + alertDesc)
+                val ok = doSendAlert(alertDesc)
+                if (!ok) {
+                  warn("Failed to send alert: " + alertDesc)
+                } else {
+                  logger.info("successfully send alert: " + alertDesc)
+                }
+                runningNumber.decrementAndGet
+                executors synchronized executors.notify
+              }
+            }
+          })
+        }
+      }
+    })
+  }
+
+  def shutdown(waitComplete: Boolean = true, timeoutMs: Long = -1): Unit = {
+    logger.info("stopping the Pooled alert thread...")
+    if (waitComplete) {
+      val startTime = System.currentTimeMillis()
+      while (
+          (alertDescQ.size() > 0 || runningNumber
+            .get() > 0) && (timeoutMs == -1 || System.currentTimeMillis() - startTime > timeoutMs)
+      ) {
+        Utils.tryQuietly(Thread.sleep(5 * 1000L))
+      }
+    }
+    executors.shutdown
+    stopped.set(true)
+    future.cancel(true)
+    logger.info("Pooled alert thread is stopped")
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsAlertDesc.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsAlertDesc.scala
new file mode 100644
index 0000000..06ef57f
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsAlertDesc.scala
@@ -0,0 +1,154 @@
+/*
+ * 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.linkis.monitor.utils.alert.ims
+
+import org.apache.linkis.monitor.constants.Constants
+import org.apache.linkis.monitor.utils.ScanUtils
+import org.apache.linkis.monitor.utils.alert.AlertDesc
+
+import org.apache.commons.collections.CollectionUtils
+import org.apache.commons.lang3.StringUtils
+
+import java.util
+import java.util.HashSet
+
+import scala.collection.JavaConverters._
+
+import ImsAlertLevel.ImsAlertLevel
+import ImsAlertWay.ImsAlertWay
+
+case class ImsAlertDesc(
+    var subSystemId: String,
+    var alertTitle: String,
+    var alertObj: String,
+    var alertInfo: String,
+    alertLevel: ImsAlertLevel = ImsAlertLevel.INFO,
+    alertIp: String,
+    canRecover: Int = 0, // 默认0,为1时,需要有对应的恢复告警
+    alertWays: util.Set[ImsAlertWay] = new HashSet[ImsAlertWay],
+    var alertReceivers: util.Set[String] = new HashSet[String],
+    var numHit: Int = 0,
+    var hitIntervalMs: Long = 0L
+) extends AlertDesc {
+
+  override val toString: String = {
+    val sb = new StringBuilder
+    sb.append("sub_system_id=").append(subSystemId).append("&alert_title=").append(alertTitle)
+    if (alertLevel != null) sb.append("&alert_level=").append(alertLevel.toString)
+    if (StringUtils.isNotEmpty(alertObj)) sb.append("&alert_obj=").append(alertObj)
+    if (StringUtils.isNotEmpty(alertInfo)) {
+      sb.append("&alert_info=")
+        .append(alertInfo)
+        .append(
+          "[freq_info] hit " + numHit + " time(s) within " + hitIntervalMs / 1000 / 60 + " mins"
+        )
+    }
+    if (canRecover == 0 || canRecover == 1) sb.append("&can_recover=").append(canRecover)
+    if (alertWays != null && alertWays.size > 0) {
+      sb.append("&alert_way=")
+      sb.append(alertWays.asScala.map(_.toString).mkString(","))
+    }
+    if (alertReceivers != null && alertReceivers.size > 0) {
+      sb.append("&alert_reciver=")
+      sb.append(alertReceivers.asScala.mkString(","))
+    }
+    if (alertIp != null) {
+      sb.append("&alert_ip=").append(alertIp)
+
+    }
+    sb.toString
+  }
+
+  val toMap: Map[String, String] = {
+    val map = scala.collection.mutable.Map[String, String]()
+    map += "sub_system_id" -> subSystemId
+    map += "alert_title" -> alertTitle
+    if (alertLevel != null) map += "alert_level" -> alertLevel.toString
+    if (StringUtils.isNotEmpty(alertObj)) map += "alert_obj" -> alertObj
+    if (StringUtils.isNotEmpty(alertInfo)) {
+      map += "alert_info" + "[freq_info] hit " + numHit + " time(s) within " + hitIntervalMs / 1000 / 60 + " mins" -> alertInfo
+    }
+    if (canRecover == 0 || canRecover == 1) map += "can_recover" -> canRecover.toString
+    if (alertWays != null && alertWays.size > 0) {
+      map += "alert_way" -> alertWays.asScala.map(_.toString).mkString(",")
+    }
+    if (alertReceivers != null && alertReceivers.size > 0) {
+      map += "alert_reciver" -> alertReceivers.asScala.mkString(",")
+    }
+    map.toMap
+  }
+
+  val toImsRequest: ImsRequest = {
+    val params = validate()
+    val alertEntity = AlertEntity(
+      params(0).asInstanceOf[String],
+      params(1).asInstanceOf[String],
+      params(
+        3
+      ) + "[freq_info] hit " + numHit + " time(s) within " + hitIntervalMs / 1000 / 60 + " mins",
+      alertWays.asScala.map(_.toString).mkString(","),
+      params(4).asInstanceOf[util.Set[String]].asScala.mkString(","),
+      alertLevel.toString,
+      params(2).asInstanceOf[String],
+      canRecover.toString
+    )
+
+    val alertEntityList = new util.ArrayList[AlertEntity]
+    alertEntityList.add(alertEntity)
+
+    ImsRequest(alertEntityList)
+  }
+
+  def validate(): Array[Any] = {
+    assert(StringUtils.isNumeric(subSystemId) && subSystemId.length == 4)
+    assert(StringUtils.isNotEmpty(alertTitle))
+    val newAlertTitle = if (alertTitle.length > 100) {
+      alertTitle.substring(0, 96) + "... ..."
+    } else {
+      alertTitle
+    }
+    val newAlertObj = if (StringUtils.isNotEmpty(alertObj) && alertObj.length >= 50) {
+      alertObj = alertObj.substring(0, 36) + "... ..."
+    } else {
+      alertObj
+    }
+    val newAlertInfo =
+      if (
+          StringUtils.isNotEmpty(alertInfo) && ScanUtils.getNumOfLines(
+            alertInfo
+          ) > Constants.ALERT_IMS_MAX_LINES
+      ) {
+        StringUtils.substring(
+          alertInfo,
+          0,
+          ScanUtils.getFirstIndexSkippingLines(alertInfo, Constants.ALERT_IMS_MAX_LINES)
+        ) + "... ...\n"
+      } else {
+        alertInfo
+      }
+    val newAlertReceivers =
+      if (CollectionUtils.isNotEmpty(alertReceivers) && alertReceivers.size > 15) {
+        alertReceivers.asScala.take(15)
+      } else {
+        alertReceivers
+      }
+
+    Array(subSystemId, newAlertTitle, newAlertObj, newAlertInfo, newAlertReceivers)
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsAlertLevel.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsAlertLevel.scala
new file mode 100644
index 0000000..10801de
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsAlertLevel.scala
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor.utils.alert.ims
+
+object ImsAlertLevel extends Enumeration {
+  type ImsAlertLevel = Value
+  val INFO = Value("5")
+  val WARN = Value("4")
+  val MINOR = Value("3")
+  val MAJOR = Value("2")
+  val CRITICAL = Value("1")
+  val CLEAR = Value("0")
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsAlertPropFileData.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsAlertPropFileData.scala
new file mode 100644
index 0000000..1166453
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsAlertPropFileData.scala
@@ -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.
+ */
+
+package org.apache.linkis.monitor.utils.alert.ims
+
+import com.fasterxml.jackson.annotation.JsonProperty
+
+case class ImsAlertPropFileData(
+    @JsonProperty("alert_title") alertTitle: String,
+    @JsonProperty("alert_info") alertInfo: String,
+    @JsonProperty("alert_way") alertWays: String,
+    @JsonProperty("alert_reciver") alertReceivers: String,
+    @JsonProperty("alert_level") alertLevel: String,
+    @JsonProperty("alert_obj") alertObj: String,
+    @JsonProperty("can_recover") canRecover: String
+)
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsAlertWay.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsAlertWay.scala
new file mode 100644
index 0000000..7f26c70
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsAlertWay.scala
@@ -0,0 +1,26 @@
+/*
+ * 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.linkis.monitor.utils.alert.ims
+
+object ImsAlertWay extends Enumeration {
+  type ImsAlertWay = Value
+  val NoAlert = Value("0")
+  val WXWork = Value("1")
+  val Email = Value("2")
+  val WeChat = Value("3")
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsRequest.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsRequest.scala
new file mode 100644
index 0000000..e497b1a
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/ImsRequest.scala
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.linkis.monitor.utils.alert.ims
+
+import java.util
+
+import com.fasterxml.jackson.annotation.JsonProperty
+
+case class ImsRequest(@JsonProperty("alertList") alertList: util.List[AlertEntity])
+
+case class AlertEntity(
+    @JsonProperty("sub_system_id") subSystemId: String,
+    @JsonProperty("alert_title") alertTitle: String,
+    @JsonProperty("alert_info") alertInfo: String,
+    @JsonProperty("alert_way") alertWays: String,
+    @JsonProperty("alert_reciver") alertReceivers: String,
+    @JsonProperty("alert_level") alertLevel: String,
+    @JsonProperty("alert_obj") alertObj: String,
+    @JsonProperty("can_recover") canRecover: String
+)
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/MonitorAlertUtils.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/MonitorAlertUtils.scala
new file mode 100644
index 0000000..67c1b03
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/MonitorAlertUtils.scala
@@ -0,0 +1,157 @@
+/*
+ * 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.linkis.monitor.utils.alert.ims
+
+import org.apache.linkis.common.utils.{JsonUtils, Logging, Utils}
+import org.apache.linkis.monitor.constants.Constants
+import org.apache.linkis.monitor.jobhistory.exception.AnomalyScannerException
+import org.apache.linkis.monitor.utils.alert.AlertDesc
+
+import org.apache.commons.io.IOUtils
+import org.apache.commons.lang3.StringUtils
+import org.apache.commons.lang3.exception.ExceptionUtils
+
+import java.io.{BufferedReader, File, FileInputStream, InputStream, InputStreamReader}
+import java.text.SimpleDateFormat
+import java.util
+import java.util.Properties
+
+import scala.collection.JavaConverters._
+
+import com.fasterxml.jackson.databind.{DeserializationFeature, ObjectMapper}
+import com.fasterxml.jackson.module.scala.DefaultScalaModule
+
+object MonitorAlertUtils extends Logging {
+
+  private val mapper = {
+    val ret = new ObjectMapper().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"))
+    ret.registerModule(DefaultScalaModule)
+    ret.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+    ret
+  }
+
+  val properties = {
+    val url = getClass.getClassLoader.getResource(Constants.ALERT_PROPS_FILE_PATH)
+    if (url == null) {
+      throw new AnomalyScannerException(
+        21304,
+        "Failed to load alerts from alert properties. Alert properties file does not exist: " + Constants.ALERT_PROPS_FILE_PATH
+      )
+    }
+    logger.info("reading alert properties from: " + url.getFile)
+    val properties = new Properties()
+    var inputStream: InputStream = null
+    var reader: InputStreamReader = null
+    var buff: BufferedReader = null
+    Utils.tryFinally {
+      Utils.tryCatch {
+        inputStream = new FileInputStream(new File(url.getFile))
+        reader = new InputStreamReader(inputStream, "UTF-8")
+        buff = new BufferedReader(reader)
+        properties.load(buff)
+      } { t =>
+        {
+          throw new AnomalyScannerException(
+            21304,
+            "Failed to load alerts from alert properties. Cause: " + ExceptionUtils.getMessage(t)
+          )
+        }
+      }
+    } {
+      IOUtils.closeQuietly(buff)
+      IOUtils.closeQuietly(reader)
+      IOUtils.closeQuietly(inputStream)
+    }
+    properties.asScala
+  }
+
+  def getAlerts(prefix: String, params: util.Map[String, String]): util.Map[String, AlertDesc] = {
+    val ret = new util.HashMap[String, AlertDesc]()
+
+    for ((k: String, v: String) <- properties) {
+      if (ret.containsKey(k)) {
+        logger.warn("found duplicate key in alert properties, accept only the first one")
+      } else if (StringUtils.startsWith(k, prefix)) {
+        val data = mapper.readValue(v, classOf[ImsAlertPropFileData])
+        var alertInfo = new String(
+          new StringBuilder().append(data.alertInfo).toString().getBytes(),
+          "utf-8"
+        ).replace("$name", data.alertReceivers)
+        val interator = params.keySet.iterator
+        while (interator.hasNext) {
+          val key = interator.next
+          val value = params.get(key)
+          alertInfo = alertInfo.replace(key, value)
+        }
+        val receivers = {
+          val set: util.Set[String] = new util.HashSet[String]
+          if (StringUtils.isNotBlank(data.alertReceivers)) {
+            data.alertReceivers.split(",").map(r => set.add(r))
+          }
+          if (!params.containsKey("$alteruser")) {
+            Constants.ALERT_DEFAULT_RECEIVERS.foreach(e => {
+              if (StringUtils.isNotBlank(e)) {
+                set.add(e)
+              }
+            })
+          } else {
+            set.add(params.get("$alteruser"))
+          }
+          if (StringUtils.isNotBlank(params.get("receiver"))) {
+            params.get("receiver").split(",").map(r => set.add(r))
+          }
+          set
+        }
+
+        val subSystemId = params.getOrDefault("subSystemId", Constants.ALERT_SUB_SYSTEM_ID)
+        val alertTitle = params.getOrDefault("title", data.alertTitle)
+        val alertLevel =
+          if (StringUtils.isNotBlank(data.alertLevel)) {
+            ImsAlertLevel.withName(params.getOrDefault("monitorLevel", data.alertLevel))
+          } else {
+            ImsAlertLevel.withName(params.getOrDefault("monitorLevel", ImsAlertLevel.WARN.toString))
+          }
+
+        val alertDesc = Utils.tryAndWarn(
+          ImsAlertDesc(
+            subSystemId,
+            alertTitle,
+            data.alertObj,
+            alertInfo,
+            alertLevel,
+            null,
+            0, {
+              val set: util.Set[ImsAlertWay.Value] = new util.HashSet[ImsAlertWay.Value]
+              if (StringUtils.isNotBlank(data.alertWays)) {
+                data.alertWays
+                  .split(",")
+                  .map(alertWayStr => set.add(ImsAlertWay.withName(alertWayStr)))
+              }
+              set
+            },
+            receivers
+          )
+        )
+        val realK = StringUtils.substringAfter(k, prefix)
+        ret.put(realK, alertDesc)
+      }
+    }
+    ret
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/PooledImsAlertSender.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/PooledImsAlertSender.scala
new file mode 100644
index 0000000..70bd155
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/PooledImsAlertSender.scala
@@ -0,0 +1,104 @@
+/*
+ * 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.linkis.monitor.utils.alert.ims
+
+import org.apache.linkis.common.utils.{JsonUtils, Logging, Utils}
+import org.apache.linkis.monitor.utils.alert.{AlertDesc, PooledAlertSender}
+import org.apache.linkis.monitor.utils.alert.AlertDesc
+import org.apache.linkis.monitor.utils.log.LogUtils
+
+import org.apache.http.client.config.RequestConfig
+import org.apache.http.client.methods.HttpPost
+import org.apache.http.entity.{ContentType, StringEntity}
+import org.apache.http.impl.client.HttpClients
+import org.apache.http.util.EntityUtils
+
+import java.text.SimpleDateFormat
+import java.util
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.scala.DefaultScalaModule
+
+class PooledImsAlertSender(alertUrl: String) extends PooledAlertSender with Logging {
+
+  protected val httpClient = HttpClients.createDefault
+
+  private val mapper =
+    new ObjectMapper().setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"))
+
+  /**
+   * describes actual actions for sending an alert
+   *
+   * @return
+   *   true if it is a success
+   */
+
+  /**
+   * describes actual actions for sending an alert
+   *
+   * @return
+   *   true if it is a success
+   */
+  override def doSendAlert(alertDesc: AlertDesc): Boolean = {
+    if (!alertDesc.isInstanceOf[ImsAlertDesc]) {
+      logger.warn("wrong alertDesc dataType: " + alertDesc.getClass.getCanonicalName)
+      return false
+    }
+    logger.info("sending an alert to IMS, information: " + alertDesc)
+    val imsRequest = alertDesc.asInstanceOf[ImsAlertDesc].toImsRequest
+
+    mapper.registerModule(DefaultScalaModule)
+    val paramContent = Utils.tryCatch(mapper.writeValueAsString(imsRequest)) { t =>
+      logger.warn("ignore alert: " + imsRequest, t)
+      return false
+    }
+    if (paramContent.isEmpty) {
+      logger.warn("alertParams is empty, will not send alarm")
+      return false
+    }
+
+    val requestConfig = RequestConfig.DEFAULT
+
+    val entity = new StringEntity(
+      paramContent,
+      ContentType.create(ContentType.APPLICATION_JSON.getMimeType, "UTF-8")
+    )
+    entity.setContentEncoding("UTF-8")
+
+    val httpPost = new HttpPost(alertUrl)
+
+    httpPost.setConfig(requestConfig)
+    httpPost.setEntity(entity)
+
+    val response = Utils.tryAndErrorMsg(httpClient.execute(httpPost))("send alert to IMS failed")
+
+    if (response != null) {
+      val responseInfo = EntityUtils.toString(response.getEntity, "UTF-8")
+      logger.info("Alert: " + paramContent + "Response: " + responseInfo)
+      LogUtils.stdOutLogger.info("Alert: " + paramContent + "Response: " + responseInfo)
+      if (response.getStatusLine.getStatusCode == 200) return true
+    }
+    false
+  }
+
+  override def shutdown(waitComplete: Boolean = true, timeoutMs: Long = -1): Unit = {
+    super.shutdown(waitComplete, timeoutMs)
+    httpClient.close
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/PooledImsAlertUtils.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/PooledImsAlertUtils.scala
new file mode 100644
index 0000000..f7917a9
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/PooledImsAlertUtils.scala
@@ -0,0 +1,110 @@
+/*
+ * 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.linkis.monitor.utils.alert.ims
+
+import org.apache.linkis.common.utils.{Logging, Utils}
+import org.apache.linkis.monitor.constants.Constants
+import org.apache.linkis.monitor.utils.alert.AlertDesc
+
+import org.apache.commons.collections.CollectionUtils
+import org.apache.commons.lang3.StringUtils
+import org.apache.commons.lang3.exception.ExceptionUtils
+
+import java.net.InetAddress
+import java.util
+import java.util.HashSet
+
+import scala.collection.JavaConverters._
+
+import ImsAlertWay.ImsAlertWay
+
+object PooledImsAlertUtils extends Logging {
+
+  private val sender: PooledImsAlertSender = {
+    val ret = new PooledImsAlertSender(Constants.ALERT_IMS_URL)
+    ret.start()
+    ret
+  }
+
+  private val localIp = InetAddress.getLocalHost.getHostAddress
+
+  def addAlertAndLogException(message: String): Unit = Utils.tryAndError(addAlert(message))
+
+  def addAlert(message: String): Unit = addExceptionAlert(message, null, null)
+
+  def addExceptionAlert(message: String, t: Throwable): Unit =
+    addExceptionAlert(message, t, null)
+
+  def addExceptionAlertAndLogException(message: String, t: Throwable): Unit =
+    Utils.tryAndError(addExceptionAlert(message, t, null))
+
+  def addExceptionAlert(message: String, t: Throwable, alertWays: util.Set[ImsAlertWay]): Unit = {
+    val alertObj =
+      if (StringUtils.isEmpty(message) && t != null) t.getMessage
+      else if (StringUtils.isEmpty(message)) {
+        throw new NullPointerException("both message and exception are null!")
+      } else {
+        message
+      }
+    val _alertWays =
+      if (CollectionUtils.isNotEmpty(alertWays)) alertWays else new HashSet[ImsAlertWay]()
+    val (alertInfo, alertLevel) = if (t != null) {
+      _alertWays.add(ImsAlertWay.Email)
+      _alertWays.add(ImsAlertWay.WXWork)
+      _alertWays.add(ImsAlertWay.WeChat)
+      (ExceptionUtils.getRootCauseMessage(t), ImsAlertLevel.MAJOR)
+    } else {
+      _alertWays.add(ImsAlertWay.WXWork)
+      (message, ImsAlertLevel.WARN)
+    }
+    val alertDesc = new ImsAlertDesc(
+      Constants.ALERT_SUB_SYSTEM_ID,
+      "BDP Alert",
+      alertObj,
+      alertInfo,
+      alertLevel,
+      localIp,
+      0,
+      _alertWays
+    )
+    addAlert(alertDesc)
+  }
+
+  def addAlert(alertDesc: AlertDesc): Unit = {
+    if (!alertDesc.isInstanceOf[ImsAlertDesc]) {
+      logger.warn("Ignore wrong alertDesc. DataType: " + alertDesc.getClass.getCanonicalName)
+    } else {
+      sender.addAlertToPool(alertDesc)
+      logger.info("successfully added alert")
+    }
+  }
+
+  def addAlertAndLogException(alertDesc: ImsAlertDesc): Unit =
+    Utils.tryAndError(addAlert(alertDesc))
+
+  def clearAlert(alertDesc: ImsAlertDesc): Unit = {
+    assert(alertDesc.canRecover == 1)
+    assert(alertDesc.alertLevel == ImsAlertLevel.CLEAR)
+    sender.addAlertToPool(alertDesc)
+  }
+
+  def shutDown(waitComplete: Boolean = true, timeoutMs: Long = -1): Unit = {
+    sender.shutdown(waitComplete, timeoutMs)
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/UserLabelAlertUtils.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/UserLabelAlertUtils.scala
new file mode 100644
index 0000000..64a587a
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/alert/ims/UserLabelAlertUtils.scala
@@ -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.
+ */
+
+package org.apache.linkis.monitor.utils.alert.ims
+
+import org.apache.linkis.common.utils.{JsonUtils, Logging, Utils}
+import org.apache.linkis.monitor.constants.Constants
+import org.apache.linkis.monitor.jobhistory.exception.AnomalyScannerException
+import org.apache.linkis.monitor.utils.alert.AlertDesc
+
+import org.apache.commons.io.IOUtils
+import org.apache.commons.lang3.StringUtils
+import org.apache.commons.lang3.exception.ExceptionUtils
+
+import java.io.{BufferedReader, File, FileInputStream, InputStream, InputStreamReader}
+import java.text.SimpleDateFormat
+import java.util
+import java.util.Properties
+
+import scala.collection.JavaConverters._
+
+import com.fasterxml.jackson.databind.{DeserializationFeature, ObjectMapper}
+import com.fasterxml.jackson.module.scala.DefaultScalaModule
+
+object UserLabelAlertUtils extends Logging {
+
+  def getAlerts(prefix: String, userCreator: String): util.Map[String, AlertDesc] = {
+    val replaceParams: util.HashMap[String, String] = new util.HashMap[String, String]
+    replaceParams.put("$userCreator", userCreator)
+    MonitorAlertUtils.getAlerts(prefix, replaceParams)
+  }
+
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/log/LogUtils.scala b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/log/LogUtils.scala
new file mode 100644
index 0000000..b63a690
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/main/scala/org/apache/linkis/monitor/utils/log/LogUtils.scala
@@ -0,0 +1,24 @@
+/*
+ * 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.linkis.monitor.utils.log
+
+import org.slf4j.LoggerFactory
+
+object LogUtils {
+  val stdOutLogger = LoggerFactory.getLogger("PlaintTextConsoleLogger")
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/test/java/org/apache/linkis/monitor/utils/alert/PooledImsAlertSenderTest.java b/linkis-extensions/linkis-et-monitor/src/test/java/org/apache/linkis/monitor/utils/alert/PooledImsAlertSenderTest.java
new file mode 100644
index 0000000..2b82037
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/test/java/org/apache/linkis/monitor/utils/alert/PooledImsAlertSenderTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.linkis.monitor.utils.alert;
+
+import org.apache.linkis.monitor.utils.alert.ims.ImsAlertDesc;
+import org.apache.linkis.monitor.utils.alert.ims.ImsAlertLevel;
+import org.apache.linkis.monitor.utils.alert.ims.ImsAlertWay;
+import org.apache.linkis.monitor.utils.alert.ims.PooledImsAlertSender;
+import org.apache.linkis.server.utils.LinkisMainHelper;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class PooledImsAlertSenderTest {
+  // @Before
+  public void before() {
+    System.getProperties().setProperty(LinkisMainHelper.SERVER_NAME_KEY(), "linkis-et-monitor");
+    System.getProperties()
+        .setProperty("log4j.configurationFile", "src/test/resources/log4j2-console.xml");
+    //        System.getProperties().setProperty("wds.linkis.server.conf",
+    // "linkis-et-monitor.properties");
+  }
+
+  // @org.junit.Test
+  public void doSendAlert() throws Exception {
+    Set<scala.Enumeration.Value> ways = new HashSet<>();
+    ways.add(ImsAlertWay.WeChat());
+    ways.add(ImsAlertWay.Email());
+
+    Set<String> receivers = new HashSet<>();
+    receivers.add("shangda, johnnwang");
+    ImsAlertDesc desc =
+        new ImsAlertDesc(
+            "5435",
+            "linkis_alert_test",
+            "linkis_alert",
+            "this is a test for linkis",
+            ImsAlertLevel.MINOR(),
+            "10.127.0.0.1",
+            0,
+            ways,
+            receivers,
+            3,
+            12);
+
+    System.out.println(desc);
+    String url = "http://172.21.0.130:10812/ims_data_access/send_alarm_by_json.do";
+
+    PooledImsAlertSender sender = new PooledImsAlertSender(url);
+    sender.doSendAlert(desc);
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/test/java/org/apache/linkis/monitor/utils/alert/PooledImsAlertUtilsTest.java b/linkis-extensions/linkis-et-monitor/src/test/java/org/apache/linkis/monitor/utils/alert/PooledImsAlertUtilsTest.java
new file mode 100644
index 0000000..c079816
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/test/java/org/apache/linkis/monitor/utils/alert/PooledImsAlertUtilsTest.java
@@ -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.
+ */
+
+package org.apache.linkis.monitor.utils.alert;
+
+import org.apache.linkis.monitor.constants.Constants;
+import org.apache.linkis.monitor.utils.alert.ims.MonitorAlertUtils;
+import org.apache.linkis.monitor.utils.alert.ims.PooledImsAlertUtils;
+import org.apache.linkis.server.utils.LinkisMainHelper;
+
+import java.util.Map;
+
+public class PooledImsAlertUtilsTest {
+  // @Before
+  public void before() {
+    System.getProperties().setProperty(LinkisMainHelper.SERVER_NAME_KEY(), "linkis-et-monitor");
+    System.getProperties()
+        .setProperty("log4j.configurationFile", "src/test/resources/log4j2-console.xml");
+    //        System.getProperties().setProperty("wds.linkis.server.conf",
+    // "linkis-et-monitor.properties");
+  }
+
+  // @Test
+  public void addAlert() throws Exception {
+    PooledImsAlertUtils.addAlert("1st test");
+    Map<String, AlertDesc> alerts =
+        MonitorAlertUtils.getAlerts((Constants.SCAN_PREFIX_ERRORCODE()), null);
+    for (Map.Entry<String, AlertDesc> kv : alerts.entrySet()) {
+      System.out.println(kv.getKey() + ": " + kv.getValue().toString());
+      PooledImsAlertUtils.addAlert(kv.getValue());
+    }
+    Thread.sleep(2000l);
+    PooledImsAlertUtils.shutDown(true, -1);
+  }
+}
diff --git a/linkis-extensions/linkis-et-monitor/src/test/resources/log4j2-console.xml b/linkis-extensions/linkis-et-monitor/src/test/resources/log4j2-console.xml
new file mode 100644
index 0000000..49eabc5
--- /dev/null
+++ b/linkis-extensions/linkis-et-monitor/src/test/resources/log4j2-console.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2019 WeBank
+  ~
+  ~ Licensed 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.
+  -->
+
+<configuration status="error" monitorInterval="30">
+    <appenders>
+        <console name="RollingFile" target="SYSTEM_OUT">
+        </console>
+        <console name="Console-Plain" target="SYSTEM_OUT">
+            <PatternLayout pattern="%m%n"/>
+        </console>
+    </appenders>
+    <loggers>
+        <root level="INFO">
+            <appender-ref ref="RollingFile"/>
+        </root>
+        <logger name="org.apache.linkis.message.builder.DefaultMessageJob" level="warn" additivity="true">
+            <appender-ref ref="RollingFile"/>
+        </logger>
+
+        <logger name="org.apache.linkis.message.scheduler.DefaultMessageExecutor" level="warn"
+                additivity="true">
+            <appender-ref ref="RollingFile"/>
+        </logger>
+        <logger name="com.netflix.loadbalancer.DynamicServerListLoadBalancer" level="warn" additivity="true">
+            <appender-ref ref="RollingFile"/>
+        </logger>
+        <logger name="PlaintTextConsoleLogger" level="INFO" additivity="false">
+            <appender-ref ref="Console-Plain"/>
+        </logger>
+    </loggers>
+</configuration>
+
diff --git a/linkis-extensions/pom.xml b/linkis-extensions/pom.xml
index 661b6be..7233141 100644
--- a/linkis-extensions/pom.xml
+++ b/linkis-extensions/pom.xml
@@ -28,6 +28,7 @@
   <packaging>pom</packaging>
   <modules>
     <module>linkis-io-file-client</module>
+    <module>linkis-et-monitor</module>
   </modules>
 
 </project>