mirror of
https://github.com/crawlab-team/crawlab.git
synced 2026-01-21 17:21:09 +01:00
加入日志异常检测
This commit is contained in:
5
backend/constants/log.go
Normal file
5
backend/constants/log.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
ErrorRegexPattern = "(?:[ :,.]|^)((?:error|exception|traceback)s?)(?:[ :,.]|$)"
|
||||
)
|
||||
@@ -29,6 +29,7 @@ type UserSetting struct {
|
||||
DingTalkRobotWebhook string `json:"ding_talk_robot_webhook" bson:"ding_talk_robot_webhook"`
|
||||
WechatRobotWebhook string `json:"wechat_robot_webhook" bson:"wechat_robot_webhook"`
|
||||
EnabledNotifications []string `json:"enabled_notifications" bson:"enabled_notifications"`
|
||||
ErrorRegexPattern string `json:"error_regex_pattern" bson:"error_regex_pattern"`
|
||||
}
|
||||
|
||||
func (user *User) Save() error {
|
||||
|
||||
@@ -48,6 +48,9 @@ export default {
|
||||
// get latest version
|
||||
await this.$store.dispatch('version/getLatestRelease')
|
||||
|
||||
// get user info
|
||||
await this.$store.dispatch('user/getInfo')
|
||||
|
||||
// remove loading-placeholder
|
||||
const elLoading = document.querySelector('#loading-placeholder')
|
||||
elLoading.remove()
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
mapGetters
|
||||
} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'LogItem',
|
||||
props: {
|
||||
@@ -45,10 +49,19 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
errorRegex: this.$utils.log.errorRegex
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('user', [
|
||||
'userInfo'
|
||||
]),
|
||||
errorRegex () {
|
||||
if (!this.userInfo.setting.error_regex_pattern) {
|
||||
return this.$utils.log.errorRegex
|
||||
}
|
||||
console.log(this.userInfo.setting.error_regex_pattern)
|
||||
return new RegExp(this.userInfo.setting.error_regex_pattern, 'i')
|
||||
},
|
||||
dataHtml () {
|
||||
let html = this.data.replace(this.errorRegex, ' <span style="font-weight: bolder; text-decoration: underline">$1</span> ')
|
||||
if (!this.searchString) return html
|
||||
|
||||
@@ -8,18 +8,19 @@
|
||||
style="margin-right: 10px"
|
||||
>
|
||||
</el-switch>
|
||||
<el-switch
|
||||
v-model="isLogAutoFetch"
|
||||
:inactive-text="$t('Auto-Refresh')"
|
||||
style="margin-right: 10px"
|
||||
>
|
||||
</el-switch>
|
||||
<!-- <el-switch-->
|
||||
<!-- v-model="isLogAutoFetch"-->
|
||||
<!-- :inactive-text="$t('Auto-Refresh')"-->
|
||||
<!-- style="margin-right: 10px"-->
|
||||
<!-- >-->
|
||||
<!-- </el-switch>-->
|
||||
<el-input
|
||||
v-model="logKeyword"
|
||||
size="small"
|
||||
suffix-icon="el-icon-search"
|
||||
:placeholder="$t('Search Log')"
|
||||
style="width: 240px; margin-right: 10px"
|
||||
clearable
|
||||
/>
|
||||
<el-button
|
||||
size="small"
|
||||
@@ -138,11 +139,11 @@ export default {
|
||||
'taskForm',
|
||||
'taskLogTotal',
|
||||
'logKeyword',
|
||||
'isLogFetchLoading'
|
||||
'isLogFetchLoading',
|
||||
'errorLogData'
|
||||
]),
|
||||
...mapGetters('task', [
|
||||
'logData',
|
||||
'errorLogData'
|
||||
'logData'
|
||||
]),
|
||||
currentLogIndex: {
|
||||
get () {
|
||||
@@ -257,9 +258,6 @@ export default {
|
||||
},
|
||||
toBottom () {
|
||||
this.$el.querySelector('.log-view').scrollTo({ top: 99999999 })
|
||||
},
|
||||
onAutoScroll () {
|
||||
|
||||
},
|
||||
toggleErrors () {
|
||||
this.isErrorsCollapsed = !this.isErrorsCollapsed
|
||||
@@ -268,15 +266,14 @@ export default {
|
||||
this.isErrorCollapsing = false
|
||||
}, 300)
|
||||
},
|
||||
onClickError (item) {
|
||||
this.currentLogIndex = item.index
|
||||
this.isLogAutoScroll = false
|
||||
const handle = setInterval(() => {
|
||||
this.isLogAutoScroll = false
|
||||
}, 10)
|
||||
setTimeout(() => {
|
||||
clearInterval(handle)
|
||||
}, 500)
|
||||
async onClickError (item) {
|
||||
const page = Math.ceil(item.seq / this.taskLogPageSize)
|
||||
this.$store.commit('task/SET_LOG_KEYWORD', '')
|
||||
this.$store.commit('task/SET_TASK_LOG_PAGE', page)
|
||||
this.$store.commit('task/SET_IS_LOG_AUTO_SCROLL', false)
|
||||
this.$store.commit('task/SET_ACTIVE_ERROR_LOG_ITEM', item)
|
||||
this.$emit('search')
|
||||
this.$st.sendEv('任务详情', '日志', '点击错误日志')
|
||||
},
|
||||
onSearchLog () {
|
||||
this.$emit('search')
|
||||
|
||||
@@ -486,6 +486,8 @@ export default {
|
||||
'Allow Sending Statistics': '允许发送统计信息',
|
||||
'General': '通用',
|
||||
'Enable Tutorial': '启用教程',
|
||||
'Error Regex Pattern': '异常正则表达式',
|
||||
'By default: ': '默认: ',
|
||||
|
||||
// 挑战
|
||||
'Challenge': '挑战',
|
||||
|
||||
@@ -30,6 +30,7 @@ const state = {
|
||||
taskLogTotal: 0,
|
||||
taskLogPage: 1,
|
||||
taskLogPageSize: 5000,
|
||||
activeErrorLogItem: {},
|
||||
// results
|
||||
resultsPageNum: 1,
|
||||
resultsPageSize: 10
|
||||
@@ -141,6 +142,9 @@ const mutations = {
|
||||
},
|
||||
SET_IS_LOG_FETCH_LOADING (state, value) {
|
||||
state.isLogFetchLoading = value
|
||||
},
|
||||
SET_ACTIVE_ERROR_LOG_ITEM (state, value) {
|
||||
state.activeErrorLogItem = value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +189,7 @@ const actions = {
|
||||
dispatch('getTaskList')
|
||||
})
|
||||
},
|
||||
getTaskLog ({ state, commit }, { id, keyword, isAutoScrolling }) {
|
||||
getTaskLog ({ state, commit }, { id, keyword }) {
|
||||
return request.get(`/tasks/${id}/log`, {
|
||||
keyword,
|
||||
page_num: state.taskLogPage,
|
||||
@@ -202,7 +206,7 @@ const actions = {
|
||||
})
|
||||
},
|
||||
getTaskErrorLog ({ state, commit }, id) {
|
||||
return request.get(`/tasks/${id}/log`, { keyword: utils.log.errorRegex.source })
|
||||
return request.get(`/tasks/${id}/error-log`, {})
|
||||
.then(response => {
|
||||
commit('SET_ERROR_LOG_DATA', response.data.data || [])
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ const regexToken = ' :,.'
|
||||
|
||||
export default {
|
||||
// errorRegex: new RegExp(`(?:[${regexToken}]|^)((?:error|exception|traceback)s?)(?:[${regexToken}]|$)`, 'gi')
|
||||
errorRegex: new RegExp(`(?:${regexToken})(error)(?:${regexToken})`, 'gi'),
|
||||
errorRegex: new RegExp(`(?:[${regexToken}]|^)((?:error|exception|traceback)s?)(?:[${regexToken}]|$)`, 'gi'),
|
||||
errorWhitelist: [
|
||||
'log_count/ERROR'
|
||||
]
|
||||
|
||||
@@ -33,8 +33,10 @@
|
||||
<!--./新增全局变量-->
|
||||
|
||||
<el-tabs v-model="activeName" @tab-click="tabActiveHandle" type="border-card">
|
||||
<!--通用-->
|
||||
<el-tab-pane :label="$t('General')" name="general">
|
||||
<el-form :model="userInfo" class="setting-form" ref="setting-form" label-width="200px" :rules="rules"
|
||||
<el-form :model="userInfo" class="setting-form" ref="setting-form" label-width="200px"
|
||||
:rules="rulesNotification"
|
||||
inline-message>
|
||||
<el-form-item prop="username" :label="$t('Username')">
|
||||
<el-input v-model="userInfo.username" disabled></el-input>
|
||||
@@ -67,8 +69,12 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<!--./通用-->
|
||||
|
||||
<!--消息通知-->
|
||||
<el-tab-pane :label="$t('Notifications')" name="notify">
|
||||
<el-form :model="userInfo" class="setting-form" ref="setting-form" label-width="200px" :rules="rules"
|
||||
<el-form :model="userInfo" class="setting-form" ref="setting-form" label-width="200px"
|
||||
:rules="rulesNotification"
|
||||
inline-message>
|
||||
<el-form-item :label="$t('Notification Trigger Timing')">
|
||||
<el-radio-group v-model="userInfo.setting.notification_trigger">
|
||||
@@ -110,6 +116,31 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<!--./消息通知-->
|
||||
|
||||
<!--日志-->
|
||||
<el-tab-pane :label="$t('Log')" name="log">
|
||||
<el-form :model="userInfo" class="setting-form" ref="log-form" label-width="200px" :rules="rulesLog"
|
||||
inline-message>
|
||||
<el-form-item :label="$t('Error Regex Pattern')" prop="setting.error_regex_pattern">
|
||||
<el-input
|
||||
v-model="userInfo.setting.error_regex_pattern"
|
||||
:placeholder="$t('By default: ') + $utils.log.errorRegex.source"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div style="text-align: right">
|
||||
<el-button type="success" size="small" @click="saveUserInfo">
|
||||
{{$t('Save')}}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<!--./日志-->
|
||||
|
||||
<!--全局变量-->
|
||||
<el-tab-pane :label="$t('Global Variable')" name="global-variable">
|
||||
<div style="text-align: right;margin-bottom: 10px">
|
||||
<el-button size="small" @click="addGlobalVariableHandle(true)"
|
||||
@@ -131,6 +162,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<!--./全局变量-->
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
@@ -175,12 +207,13 @@ export default {
|
||||
}
|
||||
return {
|
||||
userInfo: { setting: { enabled_notifications: [] } },
|
||||
rules: {
|
||||
rulesNotification: {
|
||||
password: [{ trigger: 'blur', validator: validatePass }],
|
||||
email: [{ trigger: 'blur', validator: validateEmail }],
|
||||
'setting.ding_talk_robot_webhook': [{ trigger: 'blur', validator: validateDingTalkRobotWebhook }],
|
||||
'setting.wechat_robot_webhook': [{ trigger: 'blur', validator: validateWechatRobotWebhook }]
|
||||
},
|
||||
rulesLog: {},
|
||||
isShowDingTalkAppSecret: false,
|
||||
activeName: 'general',
|
||||
addDialogVisible: false,
|
||||
@@ -233,8 +266,9 @@ export default {
|
||||
])
|
||||
},
|
||||
watch: {
|
||||
userInfoStr () {
|
||||
this.saveUserInfo()
|
||||
async userInfoStr () {
|
||||
await this.saveUserInfo()
|
||||
await this.$store.dispatch('user/getInfo')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -138,10 +138,13 @@ export default {
|
||||
'taskResultsTotalCount',
|
||||
'taskLog',
|
||||
'logKeyword',
|
||||
'isLogAutoFetch'
|
||||
'isLogAutoFetch',
|
||||
'currentLogIndex',
|
||||
'activeErrorLogItem'
|
||||
]),
|
||||
...mapGetters('task', [
|
||||
'taskResultsColumns'
|
||||
'taskResultsColumns',
|
||||
'logData'
|
||||
]),
|
||||
...mapState('file', [
|
||||
'currentPath'
|
||||
@@ -189,6 +192,21 @@ export default {
|
||||
this.$store.commit('task/SET_IS_LOG_FETCH_LOADING', value)
|
||||
}
|
||||
},
|
||||
currentLogIndex: {
|
||||
get () {
|
||||
return this.$store.state.task.currentLogIndex
|
||||
},
|
||||
set (value) {
|
||||
this.$store.commit('task/SET_CURRENT_LOG_INDEX', value)
|
||||
}
|
||||
},
|
||||
logIndexMap () {
|
||||
const map = new Map()
|
||||
this.logData.forEach((d, index) => {
|
||||
map.set(d._id, index)
|
||||
})
|
||||
return map
|
||||
},
|
||||
isRunning () {
|
||||
return ['pending', 'running'].includes(this.taskForm.status)
|
||||
}
|
||||
@@ -215,6 +233,7 @@ export default {
|
||||
this.isLogFetchLoading = true
|
||||
}
|
||||
await this.$store.dispatch('task/getTaskLog', { id: this.$route.params.id, keyword: this.logKeyword })
|
||||
this.currentLogIndex = (this.logIndexMap.get(this.activeErrorLogItem.log_id) + 1) || 0
|
||||
this.isLogFetchLoading = false
|
||||
await this.$store.dispatch('task/getTaskErrorLog', this.$route.params.id)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user