Merge branch 'dev-3.5' of git.mesalab.cn:nezha/nezha-fronted into dev-3.5
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
.batch-delete-footer{
|
||||
box-shadow: none !important;
|
||||
height: auto !important;
|
||||
display: flex;
|
||||
justify-content: space-between !important;
|
||||
}
|
||||
.batch-delete-dialog{
|
||||
transition: all 0.3s;
|
||||
.dialog-header {
|
||||
margin-bottom: 10px;
|
||||
word-wrap: break-word;
|
||||
span{
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
.nz-icon-jinggao{
|
||||
color: $--color-danger;
|
||||
}
|
||||
.nz-icon-import-failed1{
|
||||
color: $--color-danger;
|
||||
}
|
||||
.nz-icon-import-success1{
|
||||
color: $--color-success;
|
||||
}
|
||||
.el-dialog__body {
|
||||
padding: 0 20px;
|
||||
}
|
||||
.tree-header{
|
||||
background: #F6F6F6;
|
||||
padding: 7px 10px 7px 0px;
|
||||
font-weight: 600;
|
||||
.batch-delete-item-id, .batch-delete-item-name{
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
.tree-body{
|
||||
//min-height: 160px;
|
||||
//max-height: 220px;
|
||||
height: 160px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.tree-body-item{
|
||||
display: flex;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
color: $--color-text-primary;
|
||||
font-weight: 400;
|
||||
margin-bottom: 10px;
|
||||
padding-left: 10px;
|
||||
box-sizing: border-box;
|
||||
.nz-icon{
|
||||
vertical-align: middle;
|
||||
}
|
||||
.batch-delete-item-id{
|
||||
margin-left: 28px;
|
||||
margin-right: 28px;
|
||||
width: 100px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.batch-delete-item-name{
|
||||
margin-left: 14px;
|
||||
flex: 1;
|
||||
.tree-body-children:nth-of-type(1){
|
||||
margin-top: 10px;
|
||||
}
|
||||
transition: all 0.3s;
|
||||
}
|
||||
}
|
||||
.tree-body-children{
|
||||
padding-left: 25px;
|
||||
}
|
||||
.batch-delete-item-text{
|
||||
display: inline-block;
|
||||
width: calc(100% - 40px);
|
||||
vertical-align: middle;
|
||||
}
|
||||
.el-checkbox-group{
|
||||
font-size: 14px;
|
||||
.el-checkbox__label{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.nz-icon-caret-right{
|
||||
display:inline-block;
|
||||
transition: all 0.3s;
|
||||
color: $--border-color-base-focus;
|
||||
}
|
||||
.rotate90 {
|
||||
display:inline-block;
|
||||
transform: rotate(90deg);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@
|
||||
@import './common/pagination.scss';
|
||||
@import './common/searchInput.scss';
|
||||
@import './common/timePicker.scss';
|
||||
@import './common/deleteButton.scss';
|
||||
@import './common/filterSearch/filterSearch.scss';
|
||||
@import './common/panel/panelVariables.scss';
|
||||
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.el-table--border::after, .el-table--group::after, .el-table::before {
|
||||
.el-table--border::after,
|
||||
.el-table--group::after,
|
||||
.el-table::before {
|
||||
height: 0;
|
||||
}
|
||||
.introduce-view .page-header{
|
||||
.introduce-view .page-header {
|
||||
i {
|
||||
color: $--color-text-regular;
|
||||
}
|
||||
@@ -30,7 +32,7 @@
|
||||
.explore-collapse.el-collapse {
|
||||
border: none;
|
||||
.is-active .el-collapse-item {
|
||||
border-bottom: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
.el-collapse-item {
|
||||
margin-bottom: 10px;
|
||||
@@ -64,8 +66,8 @@
|
||||
}
|
||||
}
|
||||
.explore {
|
||||
.el-collapse-item__height{
|
||||
.el-collapse-item__wrap{
|
||||
.el-collapse-item__height {
|
||||
.el-collapse-item__wrap {
|
||||
overflow: unset;
|
||||
}
|
||||
}
|
||||
@@ -105,7 +107,7 @@
|
||||
padding: 0 !important;
|
||||
overflow: auto;
|
||||
}
|
||||
.metric-table{
|
||||
.metric-table {
|
||||
border-bottom: 1px solid $--border-color-light !important;
|
||||
}
|
||||
height: 100%;
|
||||
@@ -135,7 +137,7 @@
|
||||
padding-right: 20px;
|
||||
}
|
||||
span:nth-of-type(3) {
|
||||
color: #BEBEBE;
|
||||
color: #bebebe;
|
||||
}
|
||||
}
|
||||
.el-dropdown-link.explore-dropdown__item {
|
||||
@@ -145,14 +147,14 @@
|
||||
}
|
||||
.chart-room {
|
||||
width: 100%;
|
||||
height: 300px
|
||||
height: 300px;
|
||||
}
|
||||
.introduce-view {
|
||||
.info-room {
|
||||
padding: 24px;
|
||||
background-color: $--background-color-base;
|
||||
border-top: 3px solid #3274d9;
|
||||
box-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, .1), 1px 1px 0 0 rgba(0, 0, 0, .1);
|
||||
box-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, 0.1), 1px 1px 0 0 rgba(0, 0, 0, 0.1);
|
||||
flex-grow: 1;
|
||||
|
||||
.cheat-sheet-item__title {
|
||||
@@ -213,15 +215,17 @@
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.doc-content p, .doc-content.ul, .doc-content .alert {
|
||||
.doc-content p,
|
||||
.doc-content.ul,
|
||||
.doc-content .alert {
|
||||
margin: 15px 0 15px 0;
|
||||
line-height: 1.5;
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
|
||||
.doc-content .content-divider{
|
||||
.doc-content .content-divider {
|
||||
height: 1px;
|
||||
width:100%;
|
||||
width: 100%;
|
||||
border-bottom: 2px solid $--explore-border-color-bottom;
|
||||
margin: 5px 0;
|
||||
}
|
||||
@@ -332,3 +336,79 @@ article {
|
||||
margin: 10px 0 60px 0;
|
||||
}
|
||||
/*外部引用 样式end*/
|
||||
|
||||
.explore .introduce-view .title-heard.info-room{
|
||||
.logs-content {
|
||||
font-size: 16px;
|
||||
ul li{
|
||||
margin: 10px 0 10px 24px;
|
||||
list-style: disc;
|
||||
}
|
||||
p{
|
||||
margin: 15px 0;
|
||||
}
|
||||
code{
|
||||
padding: 5px;
|
||||
background-color: $--background-color-empty;
|
||||
}
|
||||
.fillbox{
|
||||
display: block;
|
||||
line-height: 25px;
|
||||
padding: 10px;
|
||||
}
|
||||
h2{
|
||||
color: #e6522c;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
> h1,.page-header {
|
||||
color: #e6522c;
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
margin-top: 15px;
|
||||
a.header-anchor {
|
||||
padding-left: 15px;
|
||||
color: gray;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
.title-heard__divider{
|
||||
margin: 5px 0 5px 0;
|
||||
}
|
||||
.page-header-label{
|
||||
p {
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
color: $--color-text-primary;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
.json-module{
|
||||
padding-left: 20px;
|
||||
}
|
||||
.box-overflow{
|
||||
overflow-x: scroll;
|
||||
}
|
||||
.logfmt-module,.json-module,.pattern-module,.regular-module,.unpack-module,.line-module,.formatting-module,.unwrapped-module{
|
||||
pre {
|
||||
border: 0;
|
||||
border-left: 0;
|
||||
border-radius: 0;
|
||||
background-color: $--background-color-empty;
|
||||
color: $--color-text-regular;
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
.img-hidden{
|
||||
width: 582px;
|
||||
overflow: hidden;
|
||||
img{
|
||||
filter: drop-shadow(-550px 0px $--color-text-primary);
|
||||
transform: translateX(570px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,17 @@
|
||||
<template v-slot:before>
|
||||
<div>
|
||||
<el-dropdown-item>
|
||||
<delete-button :type="'link'" :title="$t('overall.batchDel')" v-has="'ipam_delete'" id="account-list-batch-delete" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'ipDetails'"
|
||||
:type="'link'"
|
||||
:title="$t('overall.batchDel')"
|
||||
v-has="'ipam_delete'"
|
||||
id="account-list-batch-delete"
|
||||
:api="url"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"></delete-button>
|
||||
</el-dropdown-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -26,7 +26,16 @@
|
||||
class="nz-btn nz-btn-size-normal nz-btn-style-light margin-l-20" id="alert-list-export">
|
||||
<i class="nz-icon nz-icon-download1"></i>
|
||||
</button>
|
||||
<delete-button :clickFunction="openDelMessageBox" :delete-objs="batchDeleteObjs" @after="getAlertList" api="alert/message" v-has="['rule_alerts_delete', 'project_endpoint_alerts_delete', 'asset_alerts_delete']" :id="from+'-sub-batch-delete'"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'alertMessageTab'"
|
||||
:forceDeleteShow="false"
|
||||
:clickFunction="openDelMessageBox"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getAlertList"
|
||||
api="alert/message"
|
||||
v-has="['rule_alerts_delete', 'project_endpoint_alerts_delete', 'asset_alerts_delete']"
|
||||
:id="from+'-sub-batch-delete'"></delete-button>
|
||||
</div>
|
||||
</div>
|
||||
<alertMessageTable
|
||||
@@ -511,6 +520,9 @@ export default {
|
||||
if (response.code === 200) {
|
||||
this.$message({ type: 'success', message: this.$t('tip.deleteSuccess') })
|
||||
this.getAlertList()
|
||||
} else if (response.data && response.data.list && response.code !== 200) {
|
||||
this.delFlag = true
|
||||
this.$refs.deleteButton && this.$refs.deleteButton.showProcess(response.data.list)
|
||||
} else {
|
||||
this.$message.error(response.msg)
|
||||
}
|
||||
|
||||
@@ -55,7 +55,19 @@
|
||||
<template v-slot:before>
|
||||
<div>
|
||||
<el-dropdown-item>
|
||||
<delete-button :type="'link'" :title="$t('overall.batchDel')" id="asset-model-batch-delete" v-has="'dc_delete'" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'cabinetTab'"
|
||||
:forceDeleteShow="false"
|
||||
:type="'link'"
|
||||
:title="$t('overall.batchDel')"
|
||||
id="asset-model-batch-delete"
|
||||
v-has="'dc_delete'"
|
||||
:api="url"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</el-dropdown-item>
|
||||
</div>
|
||||
</template>
|
||||
@@ -206,6 +218,9 @@ export default {
|
||||
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.deleteSuccess') })
|
||||
self.getTableData()
|
||||
bus.$emit('cabinet-tab')
|
||||
} else if (response.data && response.data.list && response.code !== 200) {
|
||||
this.delFlag = true
|
||||
this.$refs.deleteButton && this.$refs.deleteButton.showProcess(response.data.list)
|
||||
} else {
|
||||
this.$message.error(response.msg)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,15 @@
|
||||
</button>
|
||||
</template>
|
||||
</export-excel>
|
||||
<delete-button :delete-objs="batchDeleteObjs" @after="getTableData" api="endpoint" v-has="'asset_endpoint_delete'" id="endpoint-tab-batch-delete"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'endpoint'"
|
||||
:forceDeleteShow="false"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
api="endpoint"
|
||||
v-has="'asset_endpoint_delete'"
|
||||
id="endpoint-tab-batch-delete"></delete-button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 自定义table列 -->
|
||||
|
||||
@@ -6,6 +6,78 @@
|
||||
<div v-if="this.type === 'link'" :id="id" :class="{'nz-btn-disabled' : deleteObjs.length<1}" @click="batchDelete" :title="$t('overall.delete')">
|
||||
<span><i class="nz-icon nz-icon-delete" ></i>{{title}}</span>
|
||||
</div>
|
||||
<el-dialog
|
||||
ref="batchDeleteDialog"
|
||||
id="batch-delete-dialog"
|
||||
class="batch-delete-dialog"
|
||||
title="Hint"
|
||||
v-if="dialogVisible"
|
||||
:visible.sync="dialogVisible"
|
||||
@close='handleClose'
|
||||
width="460px"
|
||||
:modal-append-to-body="true"
|
||||
:append-to-body="true"
|
||||
>
|
||||
<!-- -->
|
||||
<div class="dialog-header" style="vertical-align:top;" v-if="prepare">
|
||||
<i class="nz-icon nz-icon-jinggao" style="color:#ff0000;"></i>
|
||||
<span>Are you sure to delete these {{ this.deleteObjs.length }} Pleaces data?</span>
|
||||
</div>
|
||||
<!-- -->
|
||||
<div class="dialog-header" style="vertical-align:top;" v-if="process">
|
||||
<i class="nz-icon nz-icon-import-failed1" style="color:#ff000078;"></i>
|
||||
<span>These Assets cannot be deleted.If you want to continue to delete,please check the "Force delete" action.</span>
|
||||
</div>
|
||||
<!-- -->
|
||||
<div class="dialog-header" style="vertical-align:top;" v-if="finish">
|
||||
<i class="nz-icon nz-icon-import-success1"></i>
|
||||
<span>These Assets delete complete.</span>
|
||||
</div>
|
||||
<div class="batch-delete-tree">
|
||||
<div class="tree-header tree-body-item">
|
||||
<el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="checkAllChange" v-if="!finish"></el-checkbox>
|
||||
<div class="batch-delete-item-id" v-if="idShow">Id</div>
|
||||
<div class="batch-delete-item-name">{{$t('overall.name')}}</div>
|
||||
</div>
|
||||
<el-checkbox-group v-model="idStr" @change="checkedChange">
|
||||
<div class="tree-body">
|
||||
<div v-for="(item,index) in deleteData" :key = 'index' class="tree-body-item">
|
||||
<el-checkbox :key="item.id" :label="item.id" v-if="!finish"></el-checkbox>
|
||||
<div class="batch-delete-item-id" v-if="idShow">{{item.id}}</div>
|
||||
<div class="batch-delete-item-name" @click="showChild(item)">
|
||||
<i class="nz-icon nz-icon-caret-right" :class="item.showChildren ? 'rotate90': '' " v-if="item.relates" />
|
||||
<i :class="selectIcon(item.type)"/>
|
||||
<span class="text-ellipsis batch-delete-item-text" :title="item.name">{{item.name}}</span>
|
||||
<el-collapse-transition>
|
||||
<div v-show="item.showChildren">
|
||||
<div v-for="(relate,i) in item.relates" :key = 'i' class="tree-body-item tree-body-children" @click="showChild(relate)">
|
||||
<i class="nz-icon nz-icon-caret-right" :class="relate.showChildren ? 'rotate90': '' " v-if="relate.relates" />
|
||||
<i :class="selectIcon(relate.type)"/>
|
||||
<span class="text-ellipsis batch-delete-item-text" :title="relate.name">{{relate.name}}</span>
|
||||
<div v-for="(cildren,j) in relate.relates" :key = 'j' class="tree-body-item tree-body-children" v-if="relate.showChildren">
|
||||
<i :class="selectIcon(cildren.type)"/>
|
||||
<span class="text-ellipsis batch-delete-item-text" :title="cildren.name">{{cildren.name}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
<!-- 底部按钮 -->
|
||||
<span slot="footer" class="dialog-footer right-box__footer batch-delete-footer" v-if="!finish">
|
||||
<div v-if="forceDeleteShow">
|
||||
<el-checkbox v-model="forceDelete" text-color="black" name="type" :true-label="1" :false-label="0"></el-checkbox>
|
||||
<span class="checkTitle">Force delete</span>
|
||||
</div>
|
||||
<div>
|
||||
<button id="batch-delete-cancel" class="footer__btn footer__btn--light" style="margin-top: 8px;" type="button" @click="dialogVisible = false">{{$t('tip.no')}}</button>
|
||||
<button id="batch-delete-next" class="footer__btn" type="button" @click="batchDeleteYes">{{$t('tip.yes')}}</button>
|
||||
</div>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -13,10 +85,30 @@
|
||||
import bus from '@/libs/bus'
|
||||
export default {
|
||||
name: 'deleteButton',
|
||||
data () {
|
||||
return {
|
||||
prepare: true,
|
||||
process: false,
|
||||
finish: false,
|
||||
dialogVisible: false,
|
||||
idShow: true,
|
||||
idStr: [],
|
||||
deleteData: [],
|
||||
forceDelete: 0,
|
||||
batchDeleteObjs: [],
|
||||
checkAll: false,
|
||||
isIndeterminate: false
|
||||
}
|
||||
},
|
||||
props: {
|
||||
forceDeleteShow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
filterFunction: Function,
|
||||
deleteObjs: Array,
|
||||
api: String,
|
||||
from: String,
|
||||
clickFunction: Function,
|
||||
id: String,
|
||||
type: {
|
||||
@@ -29,36 +121,102 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
batchDelete: function () {
|
||||
this.$emit('before')
|
||||
if (this.deleteObjs.length < 1) return
|
||||
if (this.clickFunction) {
|
||||
this.clickFunction()
|
||||
return
|
||||
}
|
||||
this.$confirm(this.$t('tip.confirmBatchDelete', [this.deleteObjs.length]), {
|
||||
confirmButtonText: this.$t('tip.yes'),
|
||||
cancelButtonText: this.$t('tip.no'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$emit('before')
|
||||
const params = this.filterParam()
|
||||
this.$delete(this.api + params).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.deleteSuccess') })
|
||||
this.$emit('after')
|
||||
bus.$emit('delTableRow', this.deleteObjs.map(item => item.id))
|
||||
} else {
|
||||
this.$message.error(response.msg)
|
||||
}
|
||||
})
|
||||
this.idShow = true
|
||||
this.prepare = true
|
||||
this.process = false
|
||||
this.finish = false
|
||||
this.forceDelete = 0
|
||||
this.deleteData = this.$loadsh.cloneDeep(this.deleteObjs).map(item => {
|
||||
item.type = this.from
|
||||
return item
|
||||
})
|
||||
this.checkAll = true
|
||||
this.idStr = this.deleteData.map(item => item.id)
|
||||
this.dialogVisible = true
|
||||
},
|
||||
handleClose () {
|
||||
this.dialogVisible = false
|
||||
if (this.finish) {
|
||||
this.$emit('after')
|
||||
}
|
||||
},
|
||||
batchDeleteYes () {
|
||||
if (!this.forceDeleteShow) {
|
||||
this.forceDelete = 1
|
||||
}
|
||||
this.$delete(this.api + `?ids=${this.idStr.join(',')}&force=${this.forceDelete}`).then(res => {
|
||||
this.idShow = false
|
||||
this.prepare = false
|
||||
this.process = true
|
||||
this.finish = false
|
||||
if (res.data && res.data.list && res.code !== 200) {
|
||||
this.deleteData = res.data.list.map(item => {
|
||||
item.showChildren = true
|
||||
item.relates && item.relates.forEach(relate => {
|
||||
relate.showChildren = true
|
||||
})
|
||||
return item
|
||||
})
|
||||
} else if (res.code === 200) {
|
||||
this.idShow = false
|
||||
this.prepare = false
|
||||
this.process = false
|
||||
this.finish = true
|
||||
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.deleteSuccess') })
|
||||
// this.$emit('after')
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
filterParam: function () {
|
||||
let filterFunction = this.filterFunction
|
||||
if (!filterFunction) {
|
||||
filterFunction = (arr) => { return '?ids=' + arr.map(t => t.id).join(',') }
|
||||
showProcess (arr) {
|
||||
this.idShow = false
|
||||
this.prepare = false
|
||||
this.process = true
|
||||
this.finish = false
|
||||
this.deleteData = arr.map(item => {
|
||||
item.showChildren = true
|
||||
item.relates && item.relates.forEach(relate => {
|
||||
relate.showChildren = true
|
||||
})
|
||||
return item
|
||||
})
|
||||
this.idStr = this.deleteData.map(item => item.id)
|
||||
this.checkAll = true
|
||||
this.dialogVisible = true
|
||||
},
|
||||
checkAllChange (value) {
|
||||
const allId = this.deleteData.map(item => {
|
||||
return item.id
|
||||
})
|
||||
this.idStr = value ? allId : []
|
||||
this.isIndeterminate = false
|
||||
},
|
||||
checkedChange (value) {
|
||||
const checkedCount = value.length
|
||||
this.checkAll = checkedCount > 0
|
||||
this.isIndeterminate = checkedCount > 0 && checkedCount < this.deleteData.length
|
||||
},
|
||||
showChild (item, e) {
|
||||
e && e.stopPropagation ? e.stopPropagation() : window.event.cancelBubble = true
|
||||
item.showChildren = !item.showChildren
|
||||
},
|
||||
selectIcon (type) {
|
||||
switch (type) {
|
||||
case 'asset' : return 'nz-icon monitorColor nz-icon-overview-project'
|
||||
case 'datacenter' : return 'nz-icon monitorColor nz-icon-Datacenter2'
|
||||
case 'project' : return 'nz-icon monitorColor nz-icon-project'
|
||||
case 'module' : return 'nz-icon monitorColor nz-icon-overview-module'
|
||||
case 'endpoint' : return 'nz-icon monitorColor nz-icon-overview-endpoint'
|
||||
case 'alertrule' : return 'nz-icon monitorColor nz-icon-Alertrule'
|
||||
}
|
||||
|
||||
return filterFunction(this.deleteObjs)
|
||||
return 'nz-icon monitorColor nz-icon-module5'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,6 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
dialogFooterShow: true,
|
||||
idShow: true,
|
||||
showOne: true,
|
||||
showTwo: false,
|
||||
showThree: false,
|
||||
isDisabled: true,
|
||||
ForceDeleteShow: false,
|
||||
dialogVisible: false,
|
||||
fromRoute: fromRoute,
|
||||
// 侧滑
|
||||
rightBox: {
|
||||
@@ -50,10 +42,7 @@ export default {
|
||||
delFlag: false,
|
||||
fromBottom: false,
|
||||
operationWidth: '165', // 操作列宽
|
||||
searchCheckBox: {},
|
||||
dialogData: [],
|
||||
multipleSelection: [],
|
||||
idStr: []
|
||||
searchCheckBox: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -83,30 +72,8 @@ export default {
|
||||
isBuildIn (row) {
|
||||
return (row.buildIn && row.buildIn == 1) || (row.builtIn && row.builtIn == 1)
|
||||
},
|
||||
batchDeleteselectionChange (obj) {
|
||||
this.batchDeleteObjs = obj
|
||||
const ids = []
|
||||
this.batchDeleteObjs.forEach(function (value, index, obj) {
|
||||
ids.push(obj[index].id)
|
||||
})
|
||||
this.idStr = ids.toString()
|
||||
console.log(this.idStr, 1)
|
||||
},
|
||||
selectionChange (objs) {
|
||||
this.batchDeleteObjs = objs
|
||||
if (this.batchDeleteObjs.length > 0) {
|
||||
this.isDisabled = false
|
||||
console.log('批量删除按钮可用')
|
||||
} else {
|
||||
this.isDisabled = true
|
||||
console.log('批量删除按钮不可用')
|
||||
}
|
||||
// const ids = []
|
||||
// this.batchDeleteObjs.forEach(function (value, index, obj) {
|
||||
// ids.push(obj[index].id)
|
||||
// })
|
||||
// this.idStr = ids.toString()
|
||||
// console.log(this.idStr, 1)
|
||||
},
|
||||
getTableData (params) {
|
||||
if (params && Object.keys(params).length > 0) {
|
||||
@@ -182,94 +149,34 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
// 批量删除
|
||||
BatchDelete () {
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
// console.log(this.$refs.batchDeleteData, 2)
|
||||
console.log(this.batchDeleteObjs, 3)
|
||||
this.batchDeleteObjs.forEach((row) => {
|
||||
this.$refs.batchDeleteData.toggleRowSelection(row, true)
|
||||
})
|
||||
})
|
||||
},
|
||||
// 批量删除-确定按钮
|
||||
BatchDeleteYes (obj) {
|
||||
if (this.ForceDeleteShow) {
|
||||
this.showThree = true
|
||||
this.showOne = false
|
||||
this.showTwo = false
|
||||
this.dialogFooterShow = false
|
||||
// this.$delete(this.url + '?ids=' + this.idStr).then(response => {
|
||||
// if (response.code === 200) {
|
||||
// this.showThree = true
|
||||
// this.showOne = false
|
||||
// this.showTwo = false
|
||||
// this.dialogFooterShow = false
|
||||
// this.batchDeleteObjs = []
|
||||
// this.idStr = []
|
||||
// this.getTableData()
|
||||
// } else {
|
||||
// this.$message.error(response.msg)
|
||||
// }
|
||||
// })
|
||||
} else {
|
||||
this.showThree = false
|
||||
this.showOne = false
|
||||
this.showTwo = true
|
||||
this.dialogFooterShow = true
|
||||
}
|
||||
},
|
||||
BatchDeleteToggleSelection (val) {
|
||||
this.multipleSelection = val
|
||||
},
|
||||
// 强制删除按钮
|
||||
ForceDeleteFun () {
|
||||
this.ForceDeleteShow = true
|
||||
},
|
||||
// Dialog 关闭的回调
|
||||
handleClose (row) {
|
||||
// this.dialogVisible = false
|
||||
// this.showOne = true
|
||||
// this.showTwo = false
|
||||
// this.showThree = false
|
||||
// this.dialogFooterShow = true
|
||||
// this.ForceDeleteShow = false
|
||||
if (this.showThree) {
|
||||
this.$delete(this.url + '?ids=' + this.idStr).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.showThree = false
|
||||
this.showOne = true
|
||||
this.showTwo = false
|
||||
this.dialogFooterShow = true
|
||||
this.ForceDeleteShow = false
|
||||
this.batchDeleteObjs = []
|
||||
this.idStr = []
|
||||
this.getTableData()
|
||||
bus.$emit('delTableRow', [row.id])
|
||||
} else {
|
||||
this.$message.error(response.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
del (row) {
|
||||
const self = this
|
||||
this.$confirm(this.$t('tip.confirmDelete'), {
|
||||
confirmButtonText: this.$t('tip.yes'),
|
||||
cancelButtonText: this.$t('tip.no'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$delete(this.url + '?ids=' + row.id).then(response => {
|
||||
if (response.code === 200) {
|
||||
self.delFlag = true
|
||||
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.deleteSuccess') })
|
||||
self.getTableData()
|
||||
bus.$emit('delTableRow', [row.id])
|
||||
type: 'warning',
|
||||
beforeClose: (action, instance, done) => {
|
||||
if (action === 'confirm') {
|
||||
this.$delete(this.url + '?ids=' + row.id).then(response => {
|
||||
if (response.code === 200) {
|
||||
self.delFlag = true
|
||||
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.deleteSuccess') })
|
||||
self.getTableData()
|
||||
bus.$emit('delTableRow', [row.id])
|
||||
} else if (response.data && response.data.list && response.code !== 200) {
|
||||
self.delFlag = true
|
||||
this.$refs.deleteButton && this.$refs.deleteButton.showProcess(response.data.list)
|
||||
} else {
|
||||
this.$message.error(response.msg)
|
||||
}
|
||||
done()
|
||||
})
|
||||
} else {
|
||||
this.$message.error(response.msg)
|
||||
done()
|
||||
}
|
||||
})
|
||||
}
|
||||
}).then(() => {
|
||||
|
||||
})
|
||||
},
|
||||
newObject () {
|
||||
@@ -430,20 +337,6 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
batchDeleteObjs: {
|
||||
deep: true,
|
||||
handler (n) {
|
||||
if (n.length === 0 && this.pageObj.pageNo > 1) {
|
||||
this.pageNo(this.pageObj.pageNo - 1)
|
||||
}
|
||||
|
||||
/* if (!this.delFlag) { // 不是删除时回到顶部
|
||||
this.$refs.dataTable.bodyWrapper.scrollTop = 0
|
||||
} else {
|
||||
this.delFlag = false
|
||||
} */
|
||||
}
|
||||
},
|
||||
tableData: {
|
||||
deep: true,
|
||||
handler (n) {
|
||||
@@ -1016,6 +909,21 @@ export default {
|
||||
},
|
||||
jsonKey: 'val'
|
||||
},
|
||||
state: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
propertyName: 'state',
|
||||
type: 'string',
|
||||
defaultJson: {
|
||||
disabled: false,
|
||||
label: 'ipamState',
|
||||
name: 'State',
|
||||
readonly: true,
|
||||
type: 'select',
|
||||
val: ''
|
||||
},
|
||||
jsonKey: 'val'
|
||||
},
|
||||
name: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
@@ -1214,13 +1122,189 @@ export default {
|
||||
jsonKey: 'val'
|
||||
}
|
||||
}
|
||||
} else if (path === 'issue') {
|
||||
searchKeys = {
|
||||
// key: path 键
|
||||
// value: vue set 参数
|
||||
pageNo: { target: this.pageObj, propertyName: 'pageNo', type: 'number' },
|
||||
pageSize: { target: this.pageObj, propertyName: 'pageSize', type: 'number' },
|
||||
orderBy: { target: this.$data, propertyName: 'orderBy', type: 'string' },
|
||||
ids: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
propertyName: 'ids',
|
||||
type: 'string',
|
||||
defaultJson: {
|
||||
disabled: false,
|
||||
label: 'ids',
|
||||
name: 'ID',
|
||||
type: 'input',
|
||||
val: ''
|
||||
},
|
||||
jsonKey: 'val'
|
||||
},
|
||||
name: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
propertyName: 'name',
|
||||
type: 'string',
|
||||
defaultJson: {
|
||||
disabled: false,
|
||||
id: 'name',
|
||||
label: 'name',
|
||||
name: 'Name',
|
||||
type: 'input',
|
||||
val: ''
|
||||
},
|
||||
jsonKey: 'val'
|
||||
},
|
||||
type: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
propertyName: 'type',
|
||||
type: 'string',
|
||||
defaultJson: {
|
||||
disabled: false,
|
||||
label: 'issueType',
|
||||
name: 'Type',
|
||||
readonly: true,
|
||||
type: 'issueType',
|
||||
val: ''
|
||||
},
|
||||
jsonKey: 'val'
|
||||
},
|
||||
state: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
propertyName: 'state',
|
||||
type: 'Number',
|
||||
defaultJson: {
|
||||
disabled: false,
|
||||
label: 'issueState',
|
||||
name: 'State',
|
||||
readonly: true,
|
||||
type: 'select',
|
||||
val: ''
|
||||
},
|
||||
jsonKey: 'val'
|
||||
},
|
||||
priority: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
propertyName: 'priority',
|
||||
type: 'Number',
|
||||
defaultJson: {
|
||||
disabled: false,
|
||||
label: 'priority',
|
||||
name: 'Priority',
|
||||
type: 'select',
|
||||
val: ''
|
||||
},
|
||||
jsonKey: 'val'
|
||||
},
|
||||
assetsId: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
propertyName: 'assetsId',
|
||||
type: 'Number',
|
||||
defaultJson: {
|
||||
disabled: false,
|
||||
label: 'assetsId',
|
||||
name: 'Assets id',
|
||||
type: 'input',
|
||||
val: ''
|
||||
},
|
||||
jsonKey: 'val'
|
||||
},
|
||||
assetName: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
propertyName: 'assetName',
|
||||
type: 'string',
|
||||
defaultJson: {
|
||||
disabled: false,
|
||||
label: 'assetName',
|
||||
name: 'Assets name',
|
||||
type: 'input',
|
||||
val: ''
|
||||
},
|
||||
jsonKey: 'val'
|
||||
},
|
||||
cid: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
propertyName: 'cid',
|
||||
type: 'string',
|
||||
defaultJson: {
|
||||
disabled: false,
|
||||
label: 'cid',
|
||||
name: 'Create user',
|
||||
type: 'issue',
|
||||
val: ''
|
||||
},
|
||||
jsonKey: 'val'
|
||||
},
|
||||
uid: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
propertyName: 'uid',
|
||||
type: 'string',
|
||||
defaultJson: {
|
||||
disabled: false,
|
||||
label: 'uid',
|
||||
name: 'Update user',
|
||||
type: 'issue',
|
||||
val: ''
|
||||
},
|
||||
jsonKey: 'val'
|
||||
},
|
||||
rid: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
propertyName: 'rid',
|
||||
type: 'string',
|
||||
defaultJson: {
|
||||
disabled: false,
|
||||
label: 'rid',
|
||||
name: 'Reporter',
|
||||
type: 'issue',
|
||||
val: ''
|
||||
},
|
||||
jsonKey: 'val'
|
||||
},
|
||||
aid: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
propertyName: 'aid',
|
||||
type: 'string',
|
||||
defaultJson: {
|
||||
disabled: false,
|
||||
label: 'aid',
|
||||
name: 'Assignee',
|
||||
type: 'issue',
|
||||
val: ''
|
||||
},
|
||||
jsonKey: 'val'
|
||||
},
|
||||
starrd: {
|
||||
target: this.searchLabel,
|
||||
isSearchInput: true,
|
||||
propertyName: 'starrd',
|
||||
type: 'Number',
|
||||
defaultJson: {
|
||||
disabled: false,
|
||||
label: 'starrd',
|
||||
name: 'Starrd',
|
||||
type: 'select',
|
||||
val: ''
|
||||
},
|
||||
jsonKey: 'val'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.initQueryFromPath(searchKeys)
|
||||
},
|
||||
mounted () {
|
||||
this.$nextTick((row) => {
|
||||
})
|
||||
const pageSize = localStorage.getItem('nz-pageSize-' + localStorage.getItem('nz-username') + '-' + this.tableId)
|
||||
if (pageSize && pageSize !== 'undefined') {
|
||||
this.pageObj.pageSize = pageSize
|
||||
|
||||
@@ -36,7 +36,20 @@
|
||||
<template v-slot:before>
|
||||
<div>
|
||||
<el-dropdown-item>
|
||||
<delete-button id="alert-msg-batch-delete" v-has="'alertMessage_expired'" :api="url" :clickFunction="batchDel" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true" :title="$t('overall.batchDel')" :type="'link'"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'alertMessage'"
|
||||
:forceDeleteShow="false"
|
||||
id="alert-msg-batch-delete"
|
||||
v-has="'alertMessage_expired'"
|
||||
:api="url"
|
||||
:clickFunction="batchDel"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
:title="$t('overall.batchDel')"
|
||||
:type="'link'"
|
||||
></delete-button>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="batchDeleteObjs.length==0">
|
||||
<div id="alert-msg-batch-ack" v-has="'alertMessage_expired'" @click="batchAck"><i class="nz-icon nz-icon-queren"></i>{{$t('overall.batchAck')}}</div>
|
||||
@@ -628,6 +641,9 @@ export default {
|
||||
this.delFlag = true
|
||||
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.deleteSuccess') })
|
||||
this.getTableData()
|
||||
} else if (response.data && response.data.list && response.code !== 200) {
|
||||
this.delFlag = true
|
||||
this.$refs.deleteButton && this.$refs.deleteButton.showProcess(response.data.list)
|
||||
} else {
|
||||
this.$message.error(response.msg)
|
||||
}
|
||||
|
||||
@@ -100,7 +100,19 @@
|
||||
<template v-slot:before>
|
||||
<div>
|
||||
<el-dropdown-item>
|
||||
<delete-button :type="'link'" :title="$t('overall.batchDel')" id="alert-rule-batch-delete" v-has="'alertRule_delete'" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'alertRule'"
|
||||
:forceDeleteShow="false"
|
||||
:type="'link'"
|
||||
:title="$t('overall.batchDel')"
|
||||
id="alert-rule-batch-delete"
|
||||
v-has="'alertRule_delete'"
|
||||
:api="url"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</el-dropdown-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
<i class="nz-icon-create-square nz-icon"></i>
|
||||
</button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'alertSilence'"
|
||||
:forceDeleteShow="false"
|
||||
id="role-list-batch-delete"
|
||||
v-has="'alertSilence_expire'"
|
||||
:api="url"
|
||||
|
||||
@@ -114,12 +114,19 @@
|
||||
<el-dropdown-item>
|
||||
<div id="asset-batch-asset" v-has="'asset_edit'" @click="batchEdit"><i class="nz-icon nz-icon-batch-edit"></i>{{$t('overall.batchEdit')}}</div>
|
||||
</el-dropdown-item>
|
||||
<!-- 批量删除 -->
|
||||
<el-dropdown-item>
|
||||
<div id="asset-list-batch-delete" v-has="'asset_delete'" >
|
||||
<el-button type="text" :disabled="isDisabled" @click="BatchDelete"><i class="nz-icon nz-icon-batch-edit"></i>{{$t('overall.batchDel')}}</el-button>
|
||||
</div>
|
||||
<!-- <delete-button :type="'link'" :title="$t('overall.batchDel')" id="asset-list-batch-delete" v-has="'asset_delete'" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true" @click="deletesBtn"></delete-button> -->
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'asset'"
|
||||
:forceDeleteShow="true"
|
||||
:type="'link'"
|
||||
:title="$t('overall.batchDel')"
|
||||
id="asset-list-batch-delete"
|
||||
v-has="'asset_delete'"
|
||||
:api="url"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"></delete-button>
|
||||
</el-dropdown-item>
|
||||
</div>
|
||||
</template>
|
||||
@@ -180,82 +187,6 @@
|
||||
</transition>
|
||||
<transition name="right-box"><alert-silence-box v-if='silenceBoxShow' :alert-silence="objectSilence" @close="closeSilenceBox"></alert-silence-box>
|
||||
</transition>
|
||||
|
||||
<template>
|
||||
<!-- 批量删除 -->
|
||||
<el-dialog
|
||||
ref="dialogData"
|
||||
id="dialogID"
|
||||
title="Hint"
|
||||
v-if="dialogVisible"
|
||||
:visible.sync="dialogVisible"
|
||||
@close='handleClose'
|
||||
width="20%"
|
||||
:modal-append-to-body="true"
|
||||
:append-to-body="true"
|
||||
>
|
||||
<!-- -->
|
||||
<div class="dialog-header1" style="vertical-align:top;" v-if="showOne">
|
||||
<i class="nz-icon nz-icon-jinggao" style="color:#ff000078;"></i>
|
||||
<span>Are you sure to delete these {{ this.batchDeleteObjs.length }} Pleaces data?</span>
|
||||
</div>
|
||||
<!-- -->
|
||||
<div class="dialog-header2" style="vertical-align:top;" v-if="showTwo">
|
||||
<i class="nz-icon nz-icon-import-failed1" style="color:#ff000078;"></i>
|
||||
<span>These Assets cannot be deleted.If you want to continue to delete,please check the "Force delete" action.</span>
|
||||
</div>
|
||||
<!-- -->
|
||||
<div class="dialog-header3" style="vertical-align:top;" v-if="showThree">
|
||||
<i class="nz-icon nz-icon-import-success1" style="color:rgb(20 191 25 / 97%);"></i>
|
||||
<span>These Assets delete complete.</span>
|
||||
</div>
|
||||
<el-table
|
||||
class="branchTable"
|
||||
ref="batchDeleteData"
|
||||
:data="batchDeleteObjs"
|
||||
tooltip-effect="dark"
|
||||
style="width: 100%"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
:row-key="(row) => {return row.id}"
|
||||
@selection-change="batchDeleteselectionChange"
|
||||
@showBottomBox="(targetTab, object) => { $refs.dataList.showBottomBox(targetTab, object) }"
|
||||
@topology="topology"
|
||||
@row-dblclick="(row)=>{$emit('showBottomBox', 'panelTab', row)}"
|
||||
>
|
||||
<el-table-column
|
||||
:resizable="false"
|
||||
type="selection"
|
||||
:reserve-selection="true"
|
||||
width="55">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="idShow"
|
||||
label="ID"
|
||||
width="100"
|
||||
>
|
||||
<template slot-scope="scope">{{ scope.row.id }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="name"
|
||||
width="150">
|
||||
<template slot-scope="scope"
|
||||
>
|
||||
<i class="nz-icon nz-icon-overview-project monitorColor"></i>
|
||||
<!-- <i class="nz-icon nz-icon-overview-endpoint monitorColor"></i> -->
|
||||
{{ scope.row.name }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 底部按钮 -->
|
||||
<span slot="footer" class="dialog-footer" v-if="dialogFooterShow">
|
||||
<div class="footerBox1">
|
||||
<el-checkbox v-model="ForceDeleteShow" text-color="black" @change="this.ForceDeleteShow=!this.ForceDeleteShow" name="type"></el-checkbox>
|
||||
<span class="checkTitle">Force delete</span>
|
||||
</div>
|
||||
<el-button @click="dialogVisible = false">No</el-button>
|
||||
<el-button type="primary" @click="BatchDeleteYes">Yes</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1029,42 +960,4 @@ export default {
|
||||
/deep/ .nz-table-list{
|
||||
flex: 1;
|
||||
}
|
||||
/* del 样式 */
|
||||
/* .theme-light #dialogID .el-dialog .el-dialog__title {
|
||||
font-size: 12px;
|
||||
} */
|
||||
.el-dialog__footer {
|
||||
position: relative;
|
||||
}
|
||||
.el-dialog__footer .dialog-footer .el-checkbox{
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
left: 20px;
|
||||
|
||||
}
|
||||
.checkTitle{
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
left: 40px;
|
||||
color: #878181;
|
||||
}
|
||||
.footerBox1{
|
||||
line-height: initial;
|
||||
}
|
||||
.theme-light .el-button.is-disabled, .theme-light .el-button.is-disabled:hover, .theme-light .el-button.is-disabled:focus{
|
||||
color: #C0C4CC !important;
|
||||
|
||||
}
|
||||
.theme-light .el-button--text, #asset-list-batch-delete, .theme-light .el-button--text:focus, .theme-light .el-button--text:hover, .theme-light .el-cascader-node.in-active-path, .theme-light .el-cascader-node.is-active, .theme-light .el-cascader-node.is-selectable.in-checked-path, .theme-light .el-select-dropdown.is-multiple .el-select-dropdown__item.selected, .theme-light .el-select-dropdown__item.selected{
|
||||
color: #666 !important;
|
||||
}
|
||||
.theme-light .el-button--text:hover{
|
||||
color: #FA901C !important;
|
||||
}
|
||||
.theme-light .el-table{
|
||||
width: 100% !important;
|
||||
}
|
||||
.theme-light .el-table thead .el-table__row{
|
||||
width: 100% !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -17,7 +17,17 @@
|
||||
<button id="load-agent" class="top-tool-btn margin-r-10" type="button" @click="toDownloadAgent" v-has="'agent_edit'" :title="$t('overall.download')">
|
||||
<i class="nz-icon-download nz-icon"></i>
|
||||
</button>
|
||||
<delete-button id="promserver-list-batch-delete" v-has="'agent_delete'" :delete-objs="batchDeleteObjs" api="agent" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'agent'"
|
||||
:forceDeleteShow="true"
|
||||
id="promserver-list-batch-delete"
|
||||
v-has="'agent_delete'"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
api="agent"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</template>
|
||||
<template v-slot:default="slotProps">
|
||||
<agent-table
|
||||
|
||||
@@ -34,8 +34,16 @@
|
||||
type="button" @click="add">
|
||||
<i class="nz-icon nz-icon-create-square"></i>
|
||||
</button>
|
||||
<delete-button :delete-objs="batchDeleteObjs" @after="getTableData"
|
||||
id="meta-msg-batch-delete" v-has="'asset_label_delete'" :api="url"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'assetMeta'"
|
||||
:forceDeleteShow="false"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
id="meta-msg-batch-delete"
|
||||
v-has="'asset_label_delete'"
|
||||
:api="url"
|
||||
></delete-button>
|
||||
</template>
|
||||
<template v-slot:default="slotProps">
|
||||
<asset-meta-table
|
||||
|
||||
@@ -15,7 +15,17 @@
|
||||
type="button" @click="add">
|
||||
<i class="nz-icon-create-square nz-icon"></i>
|
||||
</button>
|
||||
<delete-button id="asset-state-list-batch-delete" v-has="'assetState_delete'" :delete-objs="batchDeleteObjs" api="/asset/stateConf" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'assetState'"
|
||||
:forceDeleteShow="false"
|
||||
id="asset-state-list-batch-delete"
|
||||
v-has="'assetState_delete'"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
api="/asset/stateConf"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</template>
|
||||
<template v-slot:default="slotProps">
|
||||
<el-table
|
||||
|
||||
@@ -14,7 +14,17 @@
|
||||
type="button" @click="add">
|
||||
<i class="nz-icon-create-square nz-icon"></i>
|
||||
</button>
|
||||
<delete-button id="account-list-batch-delete" v-has="'user_delete'" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'assetType'"
|
||||
:forceDeleteShow="false"
|
||||
id="account-list-batch-delete"
|
||||
v-has="'user_delete'"
|
||||
:api="url"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</template>
|
||||
<template v-slot="slotProps">
|
||||
<asset-type-table
|
||||
|
||||
@@ -13,7 +13,17 @@
|
||||
<button id="mib-add" v-has="'credential_add'" :title="$t('overall.createdCredentials')" class="top-tool-btn margin-r-10" type="button" @click="add">
|
||||
<i class="nz-icon-create-square nz-icon"></i>
|
||||
</button>
|
||||
<delete-button id="mib-list-batch-delete" v-has="'credential_delete'" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'credentials'"
|
||||
:forceDeleteShow="false"
|
||||
id="mib-list-batch-delete"
|
||||
v-has="'credential_delete'"
|
||||
:api="url"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</template>
|
||||
<template v-slot:default="slotProps">
|
||||
<credentials-table
|
||||
|
||||
@@ -106,7 +106,19 @@
|
||||
<template v-slot:before>
|
||||
<div>
|
||||
<el-dropdown-item>
|
||||
<delete-button :type="'link'" :title="$t('overall.batchDel')" id="account-list-batch-delete" v-has="'dc_delete'" :delete-objs="batchDeleteObjs" api="dc" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'datacenter'"
|
||||
:forceDeleteShow="true"
|
||||
:type="'link'"
|
||||
:title="$t('overall.batchDel')"
|
||||
id="account-list-batch-delete"
|
||||
v-has="'dc_delete'"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
api="dc"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</el-dropdown-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -32,7 +32,18 @@
|
||||
<template v-slot:before>
|
||||
<div>
|
||||
<el-dropdown-item>
|
||||
<delete-button :type="'link'" :title="$t('overall.batchDel')" v-has="'ipam_delete'" id="account-list-batch-delete" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'ipam'"
|
||||
:forceDeleteShow="true"
|
||||
:type="'link'"
|
||||
:title="$t('overall.batchDel')"
|
||||
v-has="'ipam_delete'"
|
||||
id="account-list-batch-delete"
|
||||
:api="url"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"></delete-button>
|
||||
</el-dropdown-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -12,7 +12,15 @@
|
||||
type="button" @click="add">
|
||||
<i class="nz-icon-create-square nz-icon"></i>
|
||||
</button>
|
||||
<delete-button id="menus-list-batch-delete" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'menus'"
|
||||
:forceDeleteShow="false"
|
||||
id="menus-list-batch-delete"
|
||||
:api="url" :delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</template>
|
||||
<template v-slot="slotProps">
|
||||
<menu-table
|
||||
|
||||
@@ -13,7 +13,17 @@
|
||||
<button id="mib-add" v-has="'snmp_mibFile_add'" :title="$t('overall.createMib')" class="top-tool-btn margin-r-10" type="button" @click="add">
|
||||
<i class="nz-icon-create-square nz-icon"></i>
|
||||
</button>
|
||||
<delete-button id="mib-list-batch-delete" v-has="'snmp_mibFile_delete'" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'mib'"
|
||||
:forceDeleteShow="false"
|
||||
id="mib-list-batch-delete"
|
||||
v-has="'snmp_mibFile_delete'"
|
||||
:api="url"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</template>
|
||||
<template v-slot:default="slotProps">
|
||||
<mib-table
|
||||
|
||||
@@ -32,7 +32,19 @@
|
||||
<template v-slot:before>
|
||||
<div>
|
||||
<el-dropdown-item>
|
||||
<delete-button :type="'link'" :title="$t('overall.batchDel')" id="account-list-batch-delete" v-has="'model_delete'" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'model'"
|
||||
:forceDeleteShow="true"
|
||||
:type="'link'"
|
||||
:title="$t('overall.batchDel')"
|
||||
id="account-list-batch-delete"
|
||||
v-has="'model_delete'"
|
||||
:api="url"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</el-dropdown-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -14,7 +14,17 @@
|
||||
type="button" @click="add">
|
||||
<i class="nz-icon-create-square nz-icon"></i>
|
||||
</button>
|
||||
<delete-button id="role-list-batch-delete" v-has="'role_delete'" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'roles'"
|
||||
:forceDeleteShow="true"
|
||||
id="role-list-batch-delete"
|
||||
v-has="'role_delete'"
|
||||
:api="url"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</template>
|
||||
<template v-slot:default="slotProps">
|
||||
<role-table
|
||||
|
||||
@@ -36,8 +36,17 @@
|
||||
<template v-slot:before>
|
||||
<div>
|
||||
<el-dropdown-item>
|
||||
<delete-button :type="'link'" :title="$t('overall.batchDel')" :delete-objs="batchDeleteObjs" @after="getTableData"
|
||||
id="alert-msg-batch-delete" v-has="'expressionTemplate_delete'" :api="url"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'chartTemp'"
|
||||
:forceDeleteShow="false"
|
||||
:type="'link'"
|
||||
:title="$t('overall.batchDel')"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
id="alert-msg-batch-delete"
|
||||
v-has="'expressionTemplate_delete'"
|
||||
:api="url"></delete-button>
|
||||
</el-dropdown-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -29,7 +29,18 @@
|
||||
<template v-slot:before>
|
||||
<div>
|
||||
<el-dropdown-item>
|
||||
<delete-button :type="'link'" :title="$t('overall.batchDel')" :delete-objs="batchDeleteObjs" @after="getTableData" id="alert-msg-batch-delete" v-has="'expressionTemplate_delete'" :api="url"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'exprTemp'"
|
||||
:forceDeleteShow="false"
|
||||
:type="'link'"
|
||||
:title="$t('overall.batchDel')"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
id="alert-msg-batch-delete"
|
||||
v-has="'expressionTemplate_delete'"
|
||||
:api="url"
|
||||
></delete-button>
|
||||
</el-dropdown-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -68,7 +68,16 @@
|
||||
type="button" @click="add">
|
||||
<i class="nz-icon-create-square nz-icon"></i>
|
||||
</button>
|
||||
<delete-button id="account-list-batch-delete" v-has="'user_delete'" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'user'"
|
||||
:forceDeleteShow="false"
|
||||
id="account-list-batch-delete"
|
||||
v-has="'user_delete'"
|
||||
:api="url" :delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</template>
|
||||
<template v-slot="slotProps">
|
||||
<user-table
|
||||
|
||||
@@ -282,53 +282,540 @@ instance_cpu_time_ns{app="fox", proc="widget", rev="4d3a513", env="prod", job="c
|
||||
<div v-else v-show="showIntroduce" class="introduce-view">
|
||||
<div class="info-room title-heard">
|
||||
<div class="col-md-9 logs-content">
|
||||
<h1>Loki Cheat Sheet</h1>
|
||||
<!-- LogQL: Log query language -->
|
||||
<h1 class="page-header" style="margin-Top:0px">LogQL: Log query language<a class="header-anchor" href="https://grafana.com/docs/loki/latest/logql/" rel="noopener noreferrer" target="_blank"><i class="nz-icon nz-icon-link1" style="font-size: 16px;" :title="$t('overall.link')"></i></a></h1>
|
||||
<div class="title-heard__divider"></div>
|
||||
<div class="page-header-label">
|
||||
<p>LogQL is Grafana Loki’s PromQL-inspired query language. Queries act as if they are a distributed grep to aggregate log sources. LogQL uses labels and operators for filtering.</p>
|
||||
<p>There are two types of LogQL queries:</p>
|
||||
<ul>
|
||||
<li><b style="color: #3C92F1" @click="jumpClick('#log-queries')">Log queries </b>return the contents of log lines.</li>
|
||||
<li><b style="color: #3C92F1" @click="jumpClick('#metric-queries')">Metric queries </b>extend log queries to calculate values based on query results.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Log queries -->
|
||||
<!-- start -->
|
||||
<div class="introduce-view__content">
|
||||
<h2>See your logs</h2>
|
||||
<h1 class="page-header" id="log-queries">Log queries</h1>
|
||||
<p>All LogQL queries contain a <b>log stream selector.</b></p>
|
||||
<div class="img-hidden"><img src="https://grafana.com/docs/loki/latest/logql/query_components.png" alt=""></div>
|
||||
<p>Optionally, the log stream selector can be followed by <b>a log pipeline</b>. A log pipeline is a set of stage expressions that are chained together and applied to the selected log streams. Each expression can filter out, parse, or mutate log lines and their respective labels.</p>
|
||||
<p>The following example shows a full log query in action:</p>
|
||||
<p><code class="fillbox">{container="query-frontend",namespace="loki-dev"} |= "metrics.go" | logfmt | duration > 10s and throughput_mb < 500</code></p>
|
||||
<p>The query is composed of:</p>
|
||||
<ul>
|
||||
<li>a log stream selector <code>{container="query-frontend",namespace="loki-dev"}</code> which targets the <code>query-frontend</code> container in the <code>loki-dev</code> namespace.</li>
|
||||
<li>a log pipeline <code>|= "metrics.go" | logfmt | duration > 10s and throughput_mb < 500</code> which will filter out log that contains the word <code>metrics.go</code>, then parses each log line to extract more labels and filter with them.</li>
|
||||
</ul>
|
||||
<pre>To avoid escaping special characters you can use the <code>`</code>(backtick) instead of<code> " </code>when quoting strings. For example <code>`\w+`</code> is the same as <code>"\\w+"</code>. This is specially useful when writing a regular expression which contains multiple <br/>backslashes that require escaping.</pre>
|
||||
</div>
|
||||
<!-- Log stream selector -->
|
||||
<div class="introduce-view__content">
|
||||
<h2 id="log-stream-selector">Log stream selector</h2>
|
||||
<div class="introduce-view__content-label">
|
||||
<p>Start by selecting a log stream from the Log labels selector</p>
|
||||
<p>Alternatively, you can write a stream selector into query field:</p>
|
||||
<span>{job="default/prometheus"}</span>
|
||||
<p>Here are some example streams from your logs:</p>
|
||||
<span>{job="systemd-journal"}</span>
|
||||
<p>The stream selector determines which log streams to include in a query’s results. A log stream is a unique source of log content, such as a file. A more granular log stream selector then reduces the number of searched streams to a manageable volume. This means that the labels passed to the log stream selector will affect the relative performance of the query’s execution.</p>
|
||||
<p>The log stream selector is specified by one or more comma-separated key-value pairs. Each key is a log label and each value is that label’s value. Curly braces (<code>{</code> and <code>}</code>) delimit the stream selector.</p>
|
||||
<p>Consider this stream selector:</p>
|
||||
<p><code class="fillbox">{app="mysql",name="mysql-backup"}</code></p>
|
||||
<p>All log streams that have both a label of <code>app</code> whose value is <code>mysql</code> and a label of <code>name</code> whose value is <code>mysql-backup</code> will be included in the query results. A stream may contain other pairs of labels and values, but only the specified pairs within the stream selector are used to determine which streams will be included within the query results.</p>
|
||||
<p>The same rules that apply for Prometheus Label Selectors apply for Grafana Loki log stream selectors.</p>
|
||||
<p>The <code>=</code> operator after the label name is a <b>label matching operator</b>. The following label matching operators are supported:</p>
|
||||
<ul>
|
||||
<li><code>=</code>: exactly equal</li>
|
||||
<li><code>!=</code>: not equal</li>
|
||||
<li><code>=~</code>: regex matches</li>
|
||||
<li><code>!~</code>: regex does not match</li>
|
||||
</ul>
|
||||
<p>Regex log stream examples:</p>
|
||||
<ul>
|
||||
<li><code>{name =~ "mysql.+"}</code></li>
|
||||
<li><code>{name !~ "mysql.+"}</code></li>
|
||||
<li><code>{name !~ `mysql-\d+`}</code></li>
|
||||
</ul>
|
||||
<p><b>Note:</b> The <code>=~</code> regex operator is fully anchored, meaning regex must match against the entire string, including newlines. The regex <code>.</code> character does not match newlines by default. If you want the regex dot character to match newlines you can use the single-line flag, like so: <code>(?s)search_term.+</code> matches <code>search_term\n.</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Log pipeline -->
|
||||
<div class="introduce-view__content">
|
||||
<h2>Combine Stream selectors</h2>
|
||||
<h2>Log pipeline</h2>
|
||||
<div class="introduce-view__content-label">
|
||||
<span>{app="cassandra",namespace="prod"}</span>
|
||||
<p>Returns all log lines from streams that both labels.</p>
|
||||
<p>A log pipeline can be appended to a log stream selector to further process and filter log streams. It is composed of a set of expressions. Each expression is executed in left to right sequence for each log line. If an expression filters out a log line, the pipeline will stop processing the current log line and start processing the next log line.</p>
|
||||
<p>Some expressions can mutate the log content and respective labels, which will be then be available for further filtering and processing in subsequent expressions. An example that mutates is the expression</p>
|
||||
<p><code v-pre class="fillbox">| line_format "{{.status_code}}"</code></p>
|
||||
<p>Log pipeline expressions fall into one of three categories:</p>
|
||||
<ul>
|
||||
<li>Filtering expressions: <b style="color: #3C92F1" @click="jumpClick('#line-filter-expressions')">line filter expressions</b> and <b style="color: #3C92F1" @click="jumpClick('#label-filter-expressions')">label filter expressions</b></li>
|
||||
<li><b style="color: #3C92F1" @click="jumpClick('#parsing-expressions')">Parsing expressions</b></li>
|
||||
<li>Formatting expressions: <b style="color: #3C92F1" @click="jumpClick('#line-format-expressions')">line format expressions</b> and <b style="color: #3C92F1" @click="jumpClick('#label-format-expressions')">label format expressions</b></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Line filter expression -->
|
||||
<div class="introduce-view__content">
|
||||
<h2>Filtering for search terms</h2>
|
||||
<h2 id="line-filter-expressions">Line filter expression</h2>
|
||||
<div class="introduce-view__content-label">
|
||||
<span>{app="cassandra"} |~ "(duration|latency)s*[d.]+"</span><br/>
|
||||
<span>{app="cassandra"!= "exact match"}</span><br/>
|
||||
<span>{app="cassandra"!= "do not match"}</span>
|
||||
<p><b style="color: #3C92F1">LogQL </b>supports exact and regular expression filters</p>
|
||||
<p>The line filter expression does a distributed <code>grep</code> over the aggregated logs from the matching log streams. It searches the contents of the log line, discarding those lines that do not match the case sensitive expression.</p>
|
||||
<p>Each line filter expression has a <b>filter operator</b> followed by text or a regular expression. These filter operators are supported:</p>
|
||||
<ul>
|
||||
<li><code>|=</code>: Log line contains string</li>
|
||||
<li><code>!=</code>: Log line does not contain string</li>
|
||||
<li><code>|~</code>: Log line contains a match to the regular expression</li>
|
||||
<li><code>!~</code>: Log line does not contain a match to the regular expression</li>
|
||||
</ul>
|
||||
<p>Line filter expression examples:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Keep log lines that have the substring “error”:</p>
|
||||
<p><code class="fillbox">|= "error"</code></p>
|
||||
<p>A complete query using this example:</p>
|
||||
<p><code class="fillbox">{job="mysql"} |= "error"</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Discard log lines that have the substring “kafka.server:type=ReplicaManager”:</p>
|
||||
<p><code class="fillbox">!= "kafka.server:type=ReplicaManager"</code></p>
|
||||
<p>A complete query using this example:</p>
|
||||
<p><code class="fillbox">{instance=~"kafka-[23]",name="kafka"} != "kafka.server:type=ReplicaManager"</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Keep log lines that contain a substring that starts with tsdb-ops and ends with io:2003. A complete query with a regular expression:</p>
|
||||
<p><code class="fillbox">{name="kafka"} |~ "tsdb-ops.*io:2003"</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Keep log lines that contain a substring that starts with error=, and is followed by 1 or more word characters. A complete query with a regular expression:</p>
|
||||
<p><code class="fillbox">{name="cassandra"} |~ `error=\w+`</code></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Filter operators can be chained. Filters are applied sequentially. Query results will have satisfied every filter. This complete query example will give results that include the string <code>error</code>, and do not include the string <code>timeout</code>.</p>
|
||||
<p><code class="fillbox">{job="mysql"} |= "error" != "timeout"</code></p>
|
||||
<p>When using <code>|~</code> and <code>!~</code>, Go (as in Golang) RE2 syntax regex may be used. The matching is case-sensitive by default. Switch to case-insensitive matching by prefixing the regular expression with <code>(?i)</code>.</p>
|
||||
<p>While line filter expressions could be placed anywhere within a log pipeline, it is almost always better to have them at the beginning. Placing them at the beginning improves the performance of the query, as it only does further processing when a line matches. For example, while the results will be the same, the query specified with</p>
|
||||
<p><code class="fillbox" v-pre>{job="mysql"} |= "error" | json | line_format "{{.err}}"</code></p>
|
||||
<p>will always run faster than</p>
|
||||
<p><code class="fillbox" v-pre>{job="mysql"} | json | line_format "{{.message}}" |= "error"</code></p>
|
||||
<p>Line filter expressions are the fastest way to filter logs once the log stream selectors have been applied.</p>
|
||||
<p>Line filter expressions have support matching IP addresses. See Matching IP addresses for details.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Label filter expression -->
|
||||
<div class="introduce-view__content">
|
||||
<h2>Count over time</h2>
|
||||
<h2 id="label-filter-expressions">Label filter expression</h2>
|
||||
<div class="introduce-view__content-label">
|
||||
<span>count_over_time{job="mysql"}[5m]</span>
|
||||
<p>This query counts all the log lines within the last five minutes for the MySQL job.</p>
|
||||
<p>Label filter expression allows filtering log line using their original and extracted labels. It can contain multiple predicates.</p>
|
||||
<p>A predicate contains a <b>label identifier</b>, an <b>operation</b> and a <b>value</b> to compare the label with.</p>
|
||||
<p>For example with <code>cluster="namespace"</code>the cluster is the label identifier, the operation is <code>=</code> and the value is “namespace”. The label identifier is always on the right side of the operation.</p>
|
||||
<p>We support multiple <b>value</b> types which are automatically inferred from the query input.</p>
|
||||
<ul>
|
||||
<li><b>String </b>is double quoted or backticked such as <code>"200"</code> or `<code>us-central1</code>`.</li>
|
||||
<li>Duration is a sequence of decimal numbers, each with optional fraction and a unit suffix, such as “300ms”, “1.5h” or “2h45m”. Valid time units are “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”.</li>
|
||||
<li><b>Number </b>are floating-point number (64bits), such as <code>250</code>, <code>89.923</code>.</li>
|
||||
<li><b>Bytes </b>is a sequence of decimal numbers, each with optional fraction and a unit suffix, such as “42MB”, “1.5Kib” or “20b”. Valid bytes units are “b”, “kib”, “kb”, “mib”, “mb”, “gib”, “gb”, “tib”, “tb”, “pib”, “pb”, “eib”, “eb”.</li>
|
||||
</ul>
|
||||
<p>String type work exactly like Prometheus label matchers use in <b style="color: #3C92F1" @click="jumpClick('#log-stream-selector')">log stream selector</b>. This means you can use the same operations (<code>=</code>,<code>!=</code>,<code>=~</code>,<code>!~</code>).</p>
|
||||
<pre>The string type is the only one that can filter out a log line with a label <code>__error__</code>.</pre>
|
||||
<p>Using Duration, Number and Bytes will convert the label value prior to comparision and support the following comparators:</p>
|
||||
<ul>
|
||||
<li><code>==</code> or <code>=</code> for equality.</li>
|
||||
<li><code>!=</code> for inequality.</li>
|
||||
<li><code>></code> and <code>>=</code> for greater than and greater than or equal.</li>
|
||||
<li><code><</code> and <code><=</code> for lesser than and lesser than or equal.</li>
|
||||
</ul>
|
||||
<p>For instance, <code>logfmt | duration > 1m and bytes_consumed > 20MB</code></p>
|
||||
<p>If the conversion of the label value fails, the log line is not filtered and an <code>__error__</code> label is added. To filters those errors see the pipeline errors section.</p>
|
||||
<p>You can chain multiple predicates using <code>and</code> and <code>or</code> which respectively express the <code>and</code> and <code>or</code> binary operations. <code>and</code> can be equivalently expressed by a comma, a space or another pipe. Label filters can be place anywhere in a log pipeline.</p>
|
||||
<p>This means that all the following expressions are equivalent:</p>
|
||||
<p><code class="fillbox">
|
||||
| duration >= 20ms or size == 20kb and method!~"2.."<br/>
|
||||
| duration >= 20ms or size == 20kb | method!~"2.."<br/>
|
||||
| duration >= 20ms or size == 20kb , method!~"2.."<br/>
|
||||
| duration >= 20ms or size == 20kb method!~"2.."
|
||||
</code></p>
|
||||
<p>By default the precedence of multiple predicates is right to left. You can wrap predicates with parenthesis to force a different precedence left to right.</p>
|
||||
<p>For example the following are equivalent.</p>
|
||||
<p><code class="fillbox">
|
||||
| duration >= 20ms or method="GET" and size <= 20KB<br/>
|
||||
| ((duration >= 20ms or method="GET") and size <= 20KB)
|
||||
</code></p>
|
||||
<p>It will evaluate first <code>duration >= 20ms or method="GET"</code>. To evaluate first <code>method="GET" and size <= 20KB</code>, make sure to use proper parenthesis as shown below.</p>
|
||||
<p><code class="fillbox">| duration >= 20ms or (method="GET" and size <= 20KB)</code></p>
|
||||
<pre>Label filter expressions are the only expression allowed after the unwrap expression. This is mainly to allow filtering errors from the metric extraction.</pre>
|
||||
<p>Label filter expressions have support matching IP addresses. See Matching IP addresses for details.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Parser expression -->
|
||||
<div class="introduce-view__content">
|
||||
<h2>Rate</h2>
|
||||
<h2 id="parsing-expressions">Parser expression</h2>
|
||||
<div class="introduce-view__content-label">
|
||||
<span>rate(({job="mysql"} |= "error" != "timeout")[10s]) </span>
|
||||
<p>This query gets the per-second rate of all non-timeout errors within the last ten seconds for the MySQL job.</p>
|
||||
<p>Parser expression can parse and extract labels from the log content. Those extracted labels can then be used for filtering using <b style="color: #3C92F1" @click="jumpClick('#label-filter-expressions')">label filter expressions</b> or for <b style="color: #3C92F1" @click="jumpClick('#metric-queries')">metric aggregations</b>.</p>
|
||||
<p>Extracted label keys are automatically sanitized by all parsers, to follow Prometheus metric name convention.(They can only contain ASCII letters and digits, as well as underscores and colons. They cannot start with a digit.)</p>
|
||||
<p>For instance, the pipeline <code>| json</code> will produce the following mapping:</p>
|
||||
<p><code class="fillbox">{ "a.b": {c: "d"}, e: "f" }</code></p>
|
||||
<p>-></p>
|
||||
<p><code class="fillbox">{a_b_c="d", e="f"}</code></p>
|
||||
<p>In case of errors, for instance if the line is not in the expected format, the log line won’t be filtered but instead will get a new <code>__error__</code> label added.</p>
|
||||
<p>If an extracted label key name already exists in the original log stream, the extracted label key will be suffixed with the <code>_extracted</code> keyword to make the distinction between the two labels. You can forcefully override the original label using a <b style="color: #3C92F1" @click="jumpClick('#label-format-expressions')">label formatter expression</b>. However if an extracted key appears twice, only the latest label value will be kept.</p>
|
||||
<p>Loki supports <b style="color: #3C92F1" @click="jumpClick('#JSON')">JSON</b>, <b style="color: #3C92F1" @click="jumpClick('#logfmt')">logfmt</b>, <b style="color: #3C92F1" @click="jumpClick('#pattern')">pattern</b>, <b style="color: #3C92F1" @click="jumpClick('#regexp')">regexp</b> and <b style="color: #3C92F1" @click="jumpClick('#unpack')">unpack</b> parsers.</p>
|
||||
<p>It’s easier to use the predefined parsers <code>json</code> and <code>logfmt</code> when you can. If you can’t, the <code>pattern</code> and <code>regexp</code> parsers can be used for log lines with an unusual structure. The <code>pattern</code> parser is easier and faster to write; it also outperforms the <code>regexp</code> parser. Multiple parsers can be used by a single log pipeline. This is useful for parsing complex logs. There are examples in <b style="color: #3C92F1" @click="jumpClick('#multiple-parsers')">Multiple parsers</b>.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- JSON -->
|
||||
<div class="introduce-view__content">
|
||||
<h2>Aggregate,count,and group </h2>
|
||||
<h2 id="JSON">JSON</h2>
|
||||
<div class="introduce-view__content-label">
|
||||
<span>sum(count_over_time({job="mysql"}[5m])) by (level)</span>
|
||||
<p>Get the count of logs during the last five minutes, grouping by level.</p>
|
||||
<p>The <b>json</b> parser operates in two modes:</p>
|
||||
<p>1. <b>without</b> parameters:</p>
|
||||
<div class="json-module">
|
||||
<p>Adding <code>| json</code> to your pipeline will extract all json properties as labels if the log line is a valid json document. Nested properties are flattened into label keys using the <code>_</code> separator.</p>
|
||||
<p>Note: <b>Arrays are skipped</b>.</p>
|
||||
<p>For example the json parsers will extract from the following document:</p>
|
||||
<pre>{
|
||||
"protocol": "HTTP/2.0",
|
||||
"servers": ["129.0.1.1","10.2.1.3"],
|
||||
"request": {
|
||||
"time": "6.032",
|
||||
"method": "GET",
|
||||
"host": "foo.grafana.net",
|
||||
"size": "55",
|
||||
"headers": {
|
||||
"Accept": "*/*",
|
||||
"User-Agent": "curl/7.68.0"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"status": 401,
|
||||
"size": "228",
|
||||
"latency_seconds": "6.031"
|
||||
}
|
||||
}</pre>
|
||||
<p>The following list of labels:</p>
|
||||
<pre>"protocol" => "HTTP/2.0"
|
||||
"request_time" => "6.032"
|
||||
"request_method" => "GET"
|
||||
"request_host" => "foo.grafana.net"
|
||||
"request_size" => "55"
|
||||
"response_status" => "401"
|
||||
"response_size" => "228"
|
||||
"response_latency_seconds" => "6.031"</pre>
|
||||
</div>
|
||||
<p>2. <b>with </b>parameters:</p>
|
||||
<div class="json-module">
|
||||
<p>Using <code>| json label="expression", another="expression"</code> in your pipeline will extract only the specified json fields to labels. You can specify one or more expressions in this way, the same as <code>label_format</code>; all expressions must be quoted.</p>
|
||||
<p>Currently, we only support field access (<code>my.field</code>, <code>my["field"]</code>) and array access (<code>list[0]</code>), and any combination of these in any level of nesting (<code>my.list[0]["field"]</code>).</p>
|
||||
<p>For example, <code>| json first_server="servers[0]", ua="request.headers[\"User-Agent\"]</code> will extract from the following document:</p>
|
||||
<pre>{
|
||||
"protocol": "HTTP/2.0",
|
||||
"servers": ["129.0.1.1","10.2.1.3"],
|
||||
"request": {
|
||||
"time": "6.032",
|
||||
"method": "GET",
|
||||
"host": "foo.grafana.net",
|
||||
"size": "55",
|
||||
"headers": {
|
||||
"Accept": "*/*",
|
||||
"User-Agent": "curl/7.68.0"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"status": 401,
|
||||
"size": "228",
|
||||
"latency_seconds": "6.031"
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
<p>The following list of labels:</p>
|
||||
<pre>"first_server" => "129.0.1.1"
|
||||
"ua" => "curl/7.68.0"</pre>
|
||||
<p>If an array or an object returned by an expression, it will be assigned to the label in json format.</p>
|
||||
<p>For example, <code>| json server_list="servers", headers="request.headers"</code> will extract:</p>
|
||||
<pre>"server_list" => `["129.0.1.1","10.2.1.3"]`
|
||||
"headers" => `{"Accept": "*/*", "User-Agent": "curl/7.68.0"}`</pre>
|
||||
<p>If the label to be extracted is same as the original JSON field, expression can be written as just <code>| json <label></code></p>
|
||||
<p>For example, to extract <code>servers</code> fields as label, expression can be written as following</p>
|
||||
<p><code>| json servers</code> will extract:</p>
|
||||
<pre>"servers" => `["129.0.1.1","10.2.1.3"]`</pre>
|
||||
<p>Note that <code>| json servers</code> is same as <code>| json servers="servers"</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- logfmt -->
|
||||
<div class="introduce-view__content">
|
||||
<h2 id="logfmt">logfmt</h2>
|
||||
<div class="introduce-view__content-label logfmt-module">
|
||||
<p>The <b>logfmt</b> parser can be added using the <code>| logfmt</code> and will extract all keys and values from the logfmt formatted log line.</p>
|
||||
<p>For example the following log line:</p>
|
||||
<pre>at=info method=GET path=/ host=grafana.net fwd="124.133.124.161" service=8ms status=200</pre>
|
||||
<p>will get those labels extracted:</p>
|
||||
<pre>"at" => "info"
|
||||
"method" => "GET"
|
||||
"path" => "/"
|
||||
"host" => "grafana.net"
|
||||
"fwd" => "124.133.124.161"
|
||||
"service" => "8ms"
|
||||
"status" => "200"</pre>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Pattern -->
|
||||
<div class="introduce-view__content">
|
||||
<h2 id="pattern">Pattern</h2>
|
||||
<div class="introduce-view__content-label pattern-module">
|
||||
<p>The pattern parser allows the explicit extraction of fields from log lines by defining a pattern expression (<code>| pattern "<pattern-expression>"</code>). The expression matches the structure of a log line.</p>
|
||||
<p>Consider this NGINX log line.</p>
|
||||
<p><code class="fillbox">0.191.12.2 - - [10/Jun/2021:09:14:29 +0000] "GET /api/plugins/versioncheck HTTP/1.1" 200 2 "-" "Go-http-client/2.0" "13.76.247.102, 34.120.177.193" "TLSv1.2" "US" ""
|
||||
</code></p>
|
||||
<p>This log line can be parsed with the expression</p>
|
||||
<p><code><ip> - - <_> "<method> <uri> <_>" <status> <size> <_> "<agent>" <_></code></p>
|
||||
<p>to extract these fields:</p>
|
||||
<pre>"ip" => "0.191.12.2"
|
||||
"method" => "GET"
|
||||
"uri" => "/api/plugins/versioncheck"
|
||||
"status" => "200"
|
||||
"size" => "2"
|
||||
"agent" => "Go-http-client/2.0"</pre>
|
||||
<p>A pattern expression is composed of captures and literals.</p>
|
||||
<p>A capture is a field name delimited by the <code><</code> and <code>></code> characters. <code><example></code> defines the field name <code>example</code>. An unnamed capture appears as <code><_></code>. <br/>The unnamed capture skips matched content.</p>
|
||||
<p>Captures are matched from the line beginning or the previous set of literals, to the line end or the next set of literals. If a capture is not matched, the pattern parser will stop.</p>
|
||||
<p>Literals can be any sequence of UTF-8 characters, including whitespace characters.</p>
|
||||
<p>By default, a pattern expression is anchored at the start of the log line. If the expression starts with literals, then the log line must also start with the same set of literals. Use <code><_></code> at the beginning of the expression if you don’t want to anchor the expression at the start.</p>
|
||||
<p>Consider the log line</p>
|
||||
<pre>level=debug ts=2021-06-10T09:24:13.472094048Z caller=logging.go:66 traceID=0568b66ad2d9294c msg="POST /loki/api/v1/push (204) 16.652862ms"</pre>
|
||||
<p>To match <code>msg="</code>, use the expression:</p>
|
||||
<pre><_> msg="<method> <path> (<status>) <latency>"</pre>
|
||||
<p>A pattern expression is invalid if</p>
|
||||
<ul>
|
||||
<li>It does not contain any named capture.</li>
|
||||
<li>It contains two consecutive captures not separated by whitespace characters.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Regular expression -->
|
||||
<div class="introduce-view__content">
|
||||
<h2 id="regexp">Regular expression</h2>
|
||||
<div class="introduce-view__content-label regular-module">
|
||||
<p>Unlike the logfmt and json, which extract implicitly all values and takes no parameters, the regexp parser takes a single parameter <code>| regexp "<re>"</code> which is the regular expression using the Golang RE2 syntax.</p>
|
||||
<p>The regular expression must contain a least one named sub-match (e.g <code>(?P<name>re)</code>), each sub-match will extract a different label.</p>
|
||||
<p>For example the parser | regexp "(?P<method>\\w+) (?P<path>[\\w|/]+) \\((?P<status>\\d+?)\\) (?P<duration>.*)" will extract from the following line:</p>
|
||||
<pre>POST /api/prom/api/v1/query_range (200) 1.5s</pre>
|
||||
<p>those labels:</p>
|
||||
<pre>"method" => "POST"
|
||||
"path" => "/api/prom/api/v1/query_range"
|
||||
"status" => "200"
|
||||
"duration" => "1.5s"</pre>
|
||||
</div>
|
||||
</div>
|
||||
<!-- unpack -->
|
||||
<div class="introduce-view__content">
|
||||
<h2 id="unpack">unpack</h2>
|
||||
<div class="introduce-view__content-label unpack-module">
|
||||
<p>The <code>unpack</code> parser parses a JSON log line, unpacking all embedded labels from Promtail’s <code>pack</code> stage. <b>A special property <code>_entry</code> will also be used to replace the original log line</b>.</p>
|
||||
<p>For example, using <code>| unpack</code> with the log line:</p>
|
||||
<pre>{
|
||||
"container": "myapp",
|
||||
"pod": "pod-3223f",
|
||||
"_entry": "original log message"
|
||||
}</pre>
|
||||
<p>extracts the <code>container</code> and <code>pod</code> labels; it sets <code>original log message</code> as the new log line.</p>
|
||||
<p>You can combine the <code>unpack</code> and <code>json</code> parsers (or any other parsers) if the original embedded log line is of a specific format.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Line format expression -->
|
||||
<div class="introduce-view__content">
|
||||
<h2 id="line-format-expressions">Line format expression</h2>
|
||||
<div class="introduce-view__content-label line-module">
|
||||
<p v-pre>The line format expression can rewrite the log line content by using the text/template format. It takes a single string parameter <code>| line_format "{{.label_name}}"</code>, which is the template format. All labels are injected variables into the template and are available to use with the <code>{{.label_name}}</code> notation.</p>
|
||||
<p>For example the following expression:</p>
|
||||
<pre v-pre>{container="frontend"} | logfmt | line_format "{{.query}} {{.duration}}"</pre>
|
||||
<p>Will extract and rewrite the log line to only contains the query and the duration of a request.</p>
|
||||
<p v-pre>You can use double quoted string for the template or backticks <code>`{{.label_name}}`</code> to avoid the need to escape special characters.</p>
|
||||
<p><code>line_format</code> also supports <code>math</code> functions. Example:</p>
|
||||
<p>If we have the following labels <code>ip=1.1.1.1</code>, <code>status=200</code> and <code>duration=3000(ms)</code>, we can divide the duration by <code>1000</code> to get the value in seconds.</p>
|
||||
<pre v-pre>{container="frontend"} | logfmt | line_format "{{.ip}} {{.status}} {{div .duration 1000}}"</pre>
|
||||
<p>The above query will give us the <code>line</code> as <code>1.1.1.1 200 3</code></p>
|
||||
<p>See template functions to learn about available functions in the template format.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Labels format expression -->
|
||||
<div class="introduce-view__content">
|
||||
<h2 id="label-format-expressions">Labels format expression</h2>
|
||||
<div class="introduce-view__content-label">
|
||||
<p>The <code>| label_format</code> expression can rename, modify or add labels. It takes as parameter a comma separated list of equality operations, enabling multiple operations at once.</p>
|
||||
<p>When both side are label identifiers, for example <code>dst=src</code>, the operation will rename the <code>src</code> label into <code>dst</code>.</p>
|
||||
<p>The right side can alternatively be a template string (double quoted or backtick), for example dst="<code v-pre>{{.status}} {{.query}}</code>", in which case the <code>dst</code> label value is replaced by the result of the text/template evaluation. This is the same template engine as the <code>| line_format</code> expression, which means labels are available as variables and you can use the same list of functions.</p>
|
||||
<p>In both cases, if the destination label doesn’t exist, then a new one is created.</p>
|
||||
<p>The renaming form <code>dst=src</code> will drop the <code>src</code> label after remapping it to the <code>dst</code> label. However, the template form will preserve the referenced labels, such that <code v-pre>dst="{{.src}}"</code> results in both <code>dst</code> and <code>src</code> having the same value.</p>
|
||||
<pre>A single label name can only appear once per expression. This means <code>| label_format foo=bar,foo="new"</code> is not allowed but you can use two expressions for the desired effect: <code>| label_format foo=bar | label_format foo="new"</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Log queries examples -->
|
||||
<h1 class="page-header">Log queries examples</h1>
|
||||
<!-- Multiple filtering -->
|
||||
<div class="introduce-view__content">
|
||||
<h2>Multiple filtering</h2>
|
||||
<div class="introduce-view__content-label">
|
||||
<p>Filtering should be done first using label matchers, then line filters (when possible) and finally using label filters. The following query demonstrate this.</p>
|
||||
<p><code v-pre class="fillbox">{cluster="ops-tools1", namespace="loki-dev", job="loki-dev/query-frontend"} |= "metrics.go" !="out of order" | logfmt | duration > 30s or status_code!="200"</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Multiple parsers -->
|
||||
<div class="introduce-view__content">
|
||||
<h2 id="multiple-parsers">Multiple parsers</h2>
|
||||
<div class="introduce-view__content-label">
|
||||
<p>To extract the method and the path of the following logfmt log line:</p>
|
||||
<p><code v-pre class="fillbox">level=debug ts=2020-10-02T10:10:42.092268913Z caller=logging.go:66 traceID=a9d4d8a928d8db1 msg="POST /api/prom/api/v1/query_range (200) 1.5s"</code></p>
|
||||
<p>You can use multiple parsers (logfmt and regexp) like this.</p>
|
||||
<p><code v-pre class="fillbox">{job="loki-ops/query-frontend"} | logfmt | line_format "{{.msg}}" | regexp "(?P<method>\\w+) (?P<path>[\\w|/]+) \\((?P<status>\\d+?)\\) (?P<duration>.*)"</code></p>
|
||||
<p>This is possible because the <code>| line_format</code> reformats the log line to become <code>POST /api/prom/api/v1/query_range (200) 1.5s</code> which can then be parsed with the <code>| regexp ...</code> parser.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Formatting -->
|
||||
<div class="introduce-view__content">
|
||||
<h2>Formatting</h2>
|
||||
<div class="introduce-view__content-label formatting-module">
|
||||
<p>The following query shows how you can reformat a log line to make it easier to read on screen.</p>
|
||||
<pre v-pre>{cluster="ops-tools1", name="querier", namespace="loki-dev"}
|
||||
|= "metrics.go" != "loki-canary"
|
||||
| logfmt
|
||||
| query != ""
|
||||
| label_format query="{{ Replace .query \"\\n\" \"\" -1 }}"
|
||||
| line_format "{{ .ts}}\t{{.duration}}\ttraceID = {{.traceID}}\t{{ printf \"%-100.100s\" .query }} "</pre>
|
||||
<p>Label formatting is used to sanitize the query while the line format reduce the amount of information and creates a tabular output.</p>
|
||||
<p>For these given log lines:</p>
|
||||
<pre class="box-overflow">level=info ts=2020-10-23T20:32:18.094668233Z caller=metrics.go:81 org_id=29 traceID=1980d41501b57b68 latency=fast query="{cluster=\"ops-tools1\", job=\"loki-ops/query-frontend\"} |= \"query_range\"" query_type=filter range_type=range length=15m0s step=7s duration=650.22401ms status=200 throughput_mb=1.529717 total_bytes_mb=0.994659
|
||||
level=info ts=2020-10-23T20:32:18.068866235Z caller=metrics.go:81 org_id=29 traceID=1980d41501b57b68 latency=fast query="{cluster=\"ops-tools1\", job=\"loki-ops/query-frontend\"} |= \"query_range\"" query_type=filter range_type=range length=15m0s step=7s duration=624.008132ms status=200 throughput_mb=0.693449 total_bytes_mb=0.432718</pre>
|
||||
<p>The result would be:</p>
|
||||
<pre>2020-10-23T20:32:18.094668233Z 650.22401ms traceID = 1980d41501b57b68 {cluster="ops-tools1", job="loki-ops/query-frontend"} |= "query_range"
|
||||
2020-10-23T20:32:18.068866235Z 624.008132ms traceID = 1980d41501b57b68 {cluster="ops-tools1", job="loki-ops/query-frontend"} |= "query_range"</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Metric queries -->
|
||||
<div class="introduce-view__content">
|
||||
<h1 class="page-header" id="metric-queries">Multiple parsers</h1>
|
||||
<div class="introduce-view__content-label">
|
||||
<p>Metric queries extend log queries by applying a function to log query results. This powerful feature creates metrics from logs.</p>
|
||||
<p>Metric queries can be used to calculate the rate of error messages or the top N log sources with the greatest quantity of logs over the last 3 hours.</p>
|
||||
<p>Combined with parsers, metric queries can also be used to calculate metrics from a sample value within the log line, such as latency or request size. All labels, including extracted ones, will be available for aggregations and generation of new series.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Range Vector aggregation -->
|
||||
<div class="introduce-view__content">
|
||||
<h1 class="page-header">Range Vector aggregation</h1>
|
||||
<div class="introduce-view__content-label">
|
||||
<p>LogQL shares the range vector concept of Prometheus. In Grafana Loki, the selected range of samples is a range of selected log or label values.</p>
|
||||
<p>The aggregation is applied over a time duration. Loki defines Time Durations with the same syntax as Prometheus.</p>
|
||||
<p>Loki supports two types of range vector aggregations: log range aggregations and unwrapped range aggregations.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Log range aggregations -->
|
||||
<div class="introduce-view__content">
|
||||
<h2>Log range aggregations</h2>
|
||||
<div class="introduce-view__content-label">
|
||||
<p>A log range aggregation is a query followed by a duration. A function is applied to aggregate the query over the duration. The duration can be placed after the log stream selector or at end of the log pipeline.</p>
|
||||
<p>The functions:</p>
|
||||
<ul>
|
||||
<li><code> rate(log-range)</code>: calculates the number of entries per second</li>
|
||||
<li><code> count_over_time(log-range)</code>: counts the entries for each log stream within the given range.</li>
|
||||
<li><code> bytes_rate(log-range)</code>: calculates the number of bytes per second for each stream.</li>
|
||||
<li><code> bytes_over_time(log-range)</code>: counts the amount of bytes used by each log stream for a given range.</li>
|
||||
<li><code> absent_over_time(log-range)</code>: returns an empty vector if the range vector passed to it has any elements and a 1-element vector with the value 1 if the range vector passed to it has no elements. (<code>absent_over_time</code> is useful for alerting on when no time series and logs stream exist for label combination for a certain amount of time.)</li>
|
||||
</ul>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Count all the log lines within the last five minutes for the MySQL job.</p>
|
||||
<p><code class="fillbox">count_over_time({job="mysql"}[5m])</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>This aggregation includes filters and parsers. It returns the per-second rate of all non-timeout errors within the last minutes per host for the MySQL job and only includes errors whose duration is above ten seconds.</p>
|
||||
<p><code class="fillbox">sum by (host) (rate({job="mysql"} |= "error" != "timeout" | json | duration > 10s [1m]))</code></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Unwrapped range aggregations -->
|
||||
<div class="introduce-view__content">
|
||||
<h2>Unwrapped range aggregations</h2>
|
||||
<div class="introduce-view__content-label">
|
||||
<p>Unwrapped ranges uses extracted labels as sample values instead of log lines. However to select which label will be used within the aggregation, the log query must end with an unwrap expression and optionally a label filter expression to discard errors.</p>
|
||||
<p>The unwrap expression is noted <code>| unwrap label_identifier</code> where the label identifier is the label name to use for extracting sample values.</p>
|
||||
<p>Since label values are string, by default a conversion into a float (64bits) will be attempted, in case of failure the <code>__error__</code> label is added to the sample. Optionally the label identifier can be wrapped by a conversion function <code>| unwrap <function>(label_identifier)</code>, which will attempt to convert the label value from a specific format.</p>
|
||||
<p>We currently support the functions:</p>
|
||||
<ul>
|
||||
<li><code>duration_seconds(label_identifier)</code> (or its short equivalent <code>duration</code>) which will convert the label value in seconds from the go duration format (e.g <code>5m</code>, <code>24s30ms</code>).</li>
|
||||
<li><code>bytes(label_identifier)</code> which will convert the label value to raw bytes applying the bytes unit (e.g. <code>5 MiB</code>, <code>3k</code>, <code>1G</code>).</li>
|
||||
</ul>
|
||||
<p>Supported function for operating over unwrapped ranges are:</p>
|
||||
<ul>
|
||||
<li><code> rate(unwrapped-range)</code>: calculates per second rate of the sum of all values in the specified interval.</li>
|
||||
<li><code> rate_counter(unwrapped-range)</code>: calculates per second rate of the values in the specified interval and treating them as “counter metric”</li>
|
||||
<li><code> sum_over_time(unwrapped-range)</code>: the sum of all values in the specified interval.</li>
|
||||
<li><code> avg_over_time(unwrapped-range)</code>: the average value of all points in the specified interval.</li>
|
||||
<li><code> max_over_time(unwrapped-range)</code>: the maximum value of all points in the specified interval.</li>
|
||||
<li><code> min_over_time(unwrapped-range)</code>: the minimum value of all points in the specified interval</li>
|
||||
<li><code> first_over_time(unwrapped-range)</code>: the first value of all points in the specified interval</li>
|
||||
<li><code> last_over_time(unwrapped-range)</code>: the last value of all points in the specified interval</li>
|
||||
<li><code> stdvar_over_time(unwrapped-range)</code>: the population standard variance of the values in the specified interval.</li>
|
||||
<li><code> stddev_over_time(unwrapped-range)</code>: the population standard deviation of the values in the specified interval.</li>
|
||||
<li><code> quantile_over_time(scalar,unwrapped-range)</code>: the φ-quantile (0 ≤ φ ≤ 1) of the values in the specified interval.</li>
|
||||
<li><code> absent_over_time(unwrapped-range)</code>: returns an empty vector if the range vector passed to it has any elements and a 1-element vector with the value 1 if the range vector passed to it has no elements. ( <code>absent_over_time</code> is useful for alerting on when no time series and logs stream exist for label combination for a certain amount of time.)</li>
|
||||
</ul>
|
||||
<p>Except for <code>sum_over_time</code>,<code>absent_over_time</code> and <code>rate</code>, unwrapped range aggregations support grouping.</p>
|
||||
<p><code class="fillbox"><aggr-op>([parameter,] <unwrapped-range>) [without|by (<label list>)]</code></p>
|
||||
<p>Which can be used to aggregate over distinct labels dimensions by including a <code>without</code> or <code>by</code> clause.</p>
|
||||
<p><code>without</code> removes the listed labels from the result vector, while all other labels are preserved the output. <code>by</code> does the opposite and drops labels that are not listed in the <code>by</code> clause, even if their label values are identical between all elements of the vector.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Unwrapped examples -->
|
||||
<div class="introduce-view__content">
|
||||
<h2>Unwrapped examples</h2>
|
||||
<div class="introduce-view__content-label unwrapped-module">
|
||||
<pre>quantile_over_time(0.99,
|
||||
{cluster="ops-tools1",container="ingress-nginx"}
|
||||
| json
|
||||
| __error__ = ""
|
||||
| unwrap request_time [1m]) by (path)</pre>
|
||||
<p>This example calculates the p99 of the nginx-ingress latency by path.</p>
|
||||
<pre>sum by (org_id) (
|
||||
sum_over_time(
|
||||
{cluster="ops-tools1",container="loki-dev"}
|
||||
|= "metrics.go"
|
||||
| logfmt
|
||||
| unwrap bytes_processed [1m])
|
||||
)</pre>
|
||||
<p>This calculates the amount of bytes processed per organization ID.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Built-in aggregation operators -->
|
||||
<div class="introduce-view__content">
|
||||
<h1 class="page-header">Built-in aggregation operators</h1>
|
||||
<div class="introduce-view__content-label">
|
||||
<p>Like PromQL, LogQL supports a subset of built-in aggregation operators that can be used to aggregate the element of a single vector, resulting in a new vector of fewer elements but with aggregated values:</p>
|
||||
<ul>
|
||||
<li><code> sum</code>: Calculate sum over labels</li>
|
||||
<li><code> avg</code>: Calculate the average over labels</li>
|
||||
<li><code> min</code>: Select minimum over labels</li>
|
||||
<li><code> max</code>: Select maximum over labels</li>
|
||||
<li><code> stddev</code>: Calculate the population standard deviation over labels</li>
|
||||
<li><code> stdvar</code>: Calculate the population standard variance over labels</li>
|
||||
<li><code> count</code>: Count number of elements in the vector</li>
|
||||
<li><code> topk</code>: Select largest k elements by sample value</li>
|
||||
<li><code> bottomk</code>: Select smallest k elements by sample value</li>
|
||||
</ul>
|
||||
<p>The aggregation operators can either be used to aggregate over all label values or a set of distinct label values by including a <code>without</code> or a <code>by</code> clause:</p>
|
||||
<p><code class="fillbox"><aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]</code></p>
|
||||
<p><code>parameter</code> is required when using <code>topk</code> and <code>bottomk</code>. <code>topk</code> and <code>bottomk</code> are different from other aggregators in that a subset of the input samples, including the original labels, are returned in the result vector.</p>
|
||||
<p><code>by</code> and <code>without</code> are only used to group the input vector. The <code>without</code> clause removes the listed labels from the resulting vector, keeping all others. The <code>by</code> clause does the opposite, dropping labels that are not listed in the clause, even if their label values are identical between all elements of the vector.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Vector aggregation examples -->
|
||||
<div class="introduce-view__content">
|
||||
<h2>Vector aggregation examples</h2>
|
||||
<div class="introduce-view__content-label">
|
||||
<p>Get the top 10 applications by the highest log throughput:</p>
|
||||
<p><code class="fillbox">topk(10,sum(rate({region="us-east1"}[5m])) by (name))</code></p>
|
||||
<p>Get the count of log lines for the last five minutes for a specified job, grouping by level:</p>
|
||||
<p><code class="fillbox">sum(count_over_time({job="mysql"}[5m])) by (level)</code></p>
|
||||
<p>Get the rate of HTTP GET requests to the <code>/home</code> endpoint for NGINX logs by region:</p>
|
||||
<p><code class="fillbox">avg(rate(({job="nginx"} |= "GET" | json | path="/home")[10s])) by (region)</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -443,6 +930,9 @@ export default {
|
||||
this.selectMetricsLogs()
|
||||
},
|
||||
methods: {
|
||||
jumpClick (id) {
|
||||
document.querySelector(id).scrollIntoView(true)
|
||||
},
|
||||
selectMetricsLogs (val, icon, label) {
|
||||
if (val) {
|
||||
this.selectIcon = icon
|
||||
|
||||
@@ -112,7 +112,19 @@
|
||||
<div id="asset-batch-asset" v-has="'asset_edit'" @click="batchEdit"><i class="nz-icon nz-icon-batch-edit"></i>{{$t('overall.batchEdit')}}</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item>
|
||||
<delete-button :type="'link'" :title="$t('overall.batchDel')" id="account-list-batch-delete" v-has="'monitor_endpoint_delete'" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'endpoint'"
|
||||
:forceDeleteShow="false"
|
||||
:type="'link'"
|
||||
:title="$t('overall.batchDel')"
|
||||
id="account-list-batch-delete"
|
||||
v-has="'monitor_endpoint_delete'"
|
||||
:api="url"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</el-dropdown-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -102,7 +102,19 @@
|
||||
<template v-slot:before>
|
||||
<div>
|
||||
<el-dropdown-item>
|
||||
<delete-button :type="'link'" :title="$t('overall.batchDel')" id="account-list-batch-delete" v-has="'monitor_module_delete'" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'module'"
|
||||
:forceDeleteShow="true"
|
||||
:type="'link'"
|
||||
:title="$t('overall.batchDel')"
|
||||
id="account-list-batch-delete"
|
||||
v-has="'monitor_module_delete'"
|
||||
:api="url"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</el-dropdown-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -15,7 +15,17 @@
|
||||
type="button" @click="add">
|
||||
<i class="nz-icon-create-square nz-icon"></i>
|
||||
</button>
|
||||
<delete-button id="account-list-batch-delete" v-has="'project_delete'" :api="url" :delete-objs="batchDeleteObjs" @after="getTableData" @before="delFlag=true"></delete-button>
|
||||
<delete-button
|
||||
ref="deleteButton"
|
||||
:from="'project'"
|
||||
:forceDeleteShow="true"
|
||||
id="account-list-batch-delete"
|
||||
v-has="'project_delete'"
|
||||
:api="url"
|
||||
:delete-objs="batchDeleteObjs"
|
||||
@after="getTableData"
|
||||
@before="delFlag=true"
|
||||
></delete-button>
|
||||
</template>
|
||||
<template v-slot="slotProps">
|
||||
<project-table
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user