优化日志,错误标记

This commit is contained in:
marvzhang
2020-02-13 11:15:51 +08:00
parent 0ea95e676a
commit 7c4ddd2824
9 changed files with 298 additions and 85 deletions

View File

@@ -1,9 +1,9 @@
<template>
<div class="log-item" :style="style">
<div class="log-item" :style="style" :class="`log-item-${index} ${active ? 'active' : ''}`">
<div class="line-no">{{index}}</div>
<div class="line-content">
<span v-if="isLogEnd" style="color: #E6A23C" class="loading-text">
{{$t('Updating log...')}}
<span v-if="isLogEnd" style="color: #E6A23C">
<span class="loading-text">{{$t('Updating log...')}}</span>
<i class="el-icon-loading"></i>
</span>
<span v-else-if="isAnsi" v-html="dataHtml"></span>
@@ -31,13 +31,15 @@ export default {
searchString: {
type: String,
default: ''
},
active: {
type: Boolean,
default: false
}
},
data () {
const token = ' :,.'
return {
errorRegex: new RegExp(`(?:[${token}]|^)((?:error|exception|traceback)s?)(?:[${token}]|$)`, 'gi')
// errorRegex: new RegExp('(error|exception|traceback)', 'gi')
errorRegex: this.$utils.log.errorRegex
}
},
computed: {
@@ -65,42 +67,56 @@ export default {
<style scoped>
.log-item {
display: table;
display: block;
}
.log-item:hover {
background: rgba(55, 57, 59, 0.5);
}
.log-item:first-child .line-no {
padding-top: 10px;
text-align: right;
}
.log-item .line-no {
display: table-cell;
display: inline-block;
width: 70px;
color: #A9B7C6;
background: #313335;
padding-right: 10px;
text-align: right;
flex-basis: 40px;
width: 70px;
}
.log-item.active .line-no {
background: #E6A23C;
color: white;
font-weight: bolder;
}
.log-item .line-content {
padding-left: 10px;
display: table-cell;
/*display: inline-block;*/
word-break: break-word;
flex-basis: calc(100% - 50px);
display: inline-block;
width: calc(100% - 70px);
white-space: nowrap;
}
.loading-text {
animation: blink;
margin-right: 5px;
animation: blink 2s ease-in infinite;
}
@keyframes blink {
0% {
opacity: 0;
opacity: 1;
}
50% {
opacity: 0.3;
}
100% {
opacity: 100%;
opacity: 1;
}
}
</style>

View File

@@ -1,43 +1,90 @@
<template>
<div class="log-view-wrapper">
<div class="log-view-container">
<div class="filter-wrapper">
<el-button
size="small"
type="primary"
icon="el-icon-download"
style="margin-right: 10px"
:disabled="isToBottom"
@click="onAutoScroll"
>
{{$t('Auto-Scroll')}}
</el-button>
<el-input
v-model="searchString"
size="small"
suffix-icon="el-icon-search"
:placeholder="$t('Search Log')"
style="width: 240px"
/>
<div class="left">
<el-button
size="small"
type="primary"
icon="el-icon-download"
style="margin-right: 10px"
:disabled="isToBottom"
@click="onAutoScroll"
>
{{$t('Auto-Scroll')}}
</el-button>
<el-input
v-model="searchString"
size="small"
suffix-icon="el-icon-search"
:placeholder="$t('Search Log')"
style="width: 240px; margin-right: 10px"
/>
</div>
<div class="right">
<el-badge
v-if="errorLogData.length > 0"
:value="errorLogData.length"
>
<el-button
type="danger"
size="small"
icon="el-icon-warning-outline"
@click="toggleErrors"
>
{{$t('Error Count')}}
</el-button>
</el-badge>
</div>
</div>
<div class="log-view-wrapper" ref="log-view-wrapper">
<virtual-list
class="log-view"
ref="log-view"
:size="6"
:remain="100"
:item="item"
:itemcount="filteredLogData.length"
:itemprops="getItemProps"
:tobottom="onToBottom"
:onscroll="onScroll"
/>
<div class="content">
<div
class="log-view-wrapper"
:class="isErrorsCollapsed ? 'errors-collapsed' : ''"
>
<virtual-list
class="log-view"
ref="log-view"
:start="currentLogIndex - 1"
:offset="0"
:size="18"
:remain="remainSize"
:item="item"
:itemcount="filteredLogData.length"
:itemprops="getItemProps"
:tobottom="onToBottom"
:onscroll="onScroll"
/>
</div>
<div
v-show="!isErrorsCollapsed && !isErrorCollapsing"
class="errors-wrapper"
:class="isErrorsCollapsed ? 'collapsed' : ''"
>
<ul class="error-list">
<li
v-for="item in errorLogData"
:key="item.index"
class="error-item"
:class="currentLogIndex === item.index ? 'active' : ''"
@click="onClickError(item)"
>
<span class="line-no">
{{item.index}}
</span>
<span class="line-content">
{{item.data}}
</span>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import {
mapState
mapState,
mapGetters
} from 'vuex'
import VirtualList from 'vue-virtual-scroll-list'
import Convert from 'ansi-to-html'
@@ -63,38 +110,38 @@ export default {
searchString: '',
isToBottom: false,
isScrolling: false,
isScrolling2nd: false
isScrolling2nd: false,
errorRegex: this.$utils.log.errorRegex,
currentOffset: 0,
isErrorsCollapsed: true,
isErrorCollapsing: false
}
},
computed: {
...mapState('task', [
'taskForm'
]),
logData () {
const data = this.data.split('\n')
.map((d, i) => {
return {
index: i + 1,
data: d
}
})
if (this.taskForm && this.taskForm.status === 'running') {
data.push({
index: data.length + 1,
data: '###LOG_END###'
})
data.push({
index: data.length + 2,
data: ''
})
...mapGetters('task', [
'logData',
'errorLogData'
]),
currentLogIndex: {
get () {
return this.$store.state.task.currentLogIndex
},
set (value) {
this.$store.commit('task/SET_CURRENT_LOG_INDEX', value)
}
return data
},
filteredLogData () {
return this.logData.filter(d => {
if (!this.searchString) return true
return !!d.data.toLowerCase().match(this.searchString.toLowerCase())
})
},
remainSize () {
const height = document.querySelector('body').clientHeight
return (height - 240) / 18
}
},
watch: {
@@ -113,6 +160,7 @@ export default {
index: logItem.index,
data: isAnsi ? convert.toHtml(logItem.data) : logItem.data,
searchString: this.searchString,
active: logItem.active,
isAnsi
}
}
@@ -144,6 +192,23 @@ export default {
},
onAutoScroll () {
this.toBottom()
},
toggleErrors () {
this.isErrorsCollapsed = !this.isErrorsCollapsed
this.isErrorCollapsing = true
setTimeout(() => {
this.isErrorCollapsing = false
}, 300)
},
onClickError (item) {
this.currentLogIndex = item.index
this.isToBottom = false
const handle = setInterval(() => {
this.isToBottom = false
}, 10)
setTimeout(() => {
clearInterval(handle)
}, 500)
}
},
mounted () {
@@ -151,7 +216,7 @@ export default {
if (this.isToBottom) {
this.toBottom()
}
}, 100)
}, 500)
},
destroyed () {
clearInterval(this.handle)
@@ -160,15 +225,94 @@ export default {
</script>
<style scoped>
.filter-wrapper {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.content {
display: block;
}
.log-view-wrapper {
float: left;
flex-basis: calc(100% - 240px);
width: calc(100% - 300px);
transition: width 0.3s;
}
.log-view-wrapper.errors-collapsed {
flex-basis: 100%;
width: 100%;
}
.log-view {
margin-top: 0 !important;
overflow-y: scroll !important;
list-style: none;
color: #A9B7C6;
background: #2B2B2B;
border: none;
}
.filter-wrapper {
margin-bottom: 10px;
.errors-wrapper {
float: left;
display: inline-block;
margin: 0;
padding: 0;
flex-basis: 240px;
width: 300px;
transition: opacity 0.3s;
border-top: 1px solid #DCDFE6;
border-right: 1px solid #DCDFE6;
border-bottom: 1px solid #DCDFE6;
height: calc(100vh - 240px);
font-size: 16px;
}
.errors-wrapper.collapsed {
width: 0;
}
.errors-wrapper .error-list {
list-style: none;
padding: 0;
margin: 0;
}
.errors-wrapper .error-list .error-item {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
/*height: 18px;*/
border-bottom: 1px solid white;
padding: 5px 0;
background: #F56C6C;
color: white;
cursor: pointer;
}
.errors-wrapper .error-list .error-item.active {
background: #E6A23C;
font-weight: bolder;
text-decoration: underline;
}
.errors-wrapper .error-list .error-item:hover {
font-weight: bolder;
text-decoration: underline;
}
.errors-wrapper .error-list .error-item .line-no {
display: inline-block;
text-align: right;
width: 70px;
}
.errors-wrapper .error-list .error-item .line-content {
display: inline;
width: calc(100% - 70px);
padding-left: 10px;
}
</style>