NEZ-858 feat: 新版explore-log
This commit is contained in:
@@ -24,7 +24,7 @@
|
|||||||
"@topology/sequence-diagram": "^0.3.0",
|
"@topology/sequence-diagram": "^0.3.0",
|
||||||
"axios": "^0.19.0",
|
"axios": "^0.19.0",
|
||||||
"cytoscape": "^3.15.2",
|
"cytoscape": "^3.15.2",
|
||||||
"echarts": "^5.0.1",
|
"echarts": "^5.1.2",
|
||||||
"element-ui": "^2.15.3",
|
"element-ui": "^2.15.3",
|
||||||
"file-saver": "^2.0.2",
|
"file-saver": "^2.0.2",
|
||||||
"leaflet": "^1.7.1",
|
"leaflet": "^1.7.1",
|
||||||
|
|||||||
@@ -54,6 +54,7 @@
|
|||||||
.top-tool-left {
|
.top-tool-left {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.top-tool-btn-group {
|
.top-tool-btn-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
.top-tool-btn:not(:last-of-type):not(:first-of-type) {
|
.top-tool-btn:not(:last-of-type):not(:first-of-type) {
|
||||||
@@ -110,7 +111,6 @@
|
|||||||
width: auto;
|
width: auto;
|
||||||
min-width: 36px;
|
min-width: 36px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.top-tools--sub {
|
.top-tools--sub {
|
||||||
.top-tool-left {
|
.top-tool-left {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -706,7 +706,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.top-tool-btn-group {
|
.top-tool-btn-group {
|
||||||
margin-left: 10px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
.top-tool-btn:not(:last-of-type):not(:first-of-type) {
|
.top-tool-btn:not(:last-of-type):not(:first-of-type) {
|
||||||
border-left: none;
|
border-left: none;
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ export default {
|
|||||||
|
|
||||||
.chart-unit{
|
.chart-unit{
|
||||||
width: 100px;
|
width: 100px;
|
||||||
margin: 0 20px 0 0;
|
|
||||||
}
|
}
|
||||||
.chart-unit.el-cascader .el-input.is-focus .el-input__inner {
|
.chart-unit.el-cascader .el-input.is-focus .el-input__inner {
|
||||||
border-color: #FBCEA4;
|
border-color: #FBCEA4;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const cn = {
|
|||||||
oneDay: '1 天',
|
oneDay: '1 天',
|
||||||
twoDay: '2 天',
|
twoDay: '2 天',
|
||||||
week: '1 周',
|
week: '1 周',
|
||||||
|
time: '时间',
|
||||||
folder: '文件夹',
|
folder: '文件夹',
|
||||||
key: '键名',
|
key: '键名',
|
||||||
state: '状态',
|
state: '状态',
|
||||||
@@ -64,6 +65,7 @@ const cn = {
|
|||||||
other: '其他',
|
other: '其他',
|
||||||
about: '关于',
|
about: '关于',
|
||||||
query: '查询',
|
query: '查询',
|
||||||
|
logLabels: '日志标签',
|
||||||
account: '账号',
|
account: '账号',
|
||||||
back: '返回',
|
back: '返回',
|
||||||
unavailable: '不可用',
|
unavailable: '不可用',
|
||||||
@@ -114,6 +116,7 @@ const cn = {
|
|||||||
},
|
},
|
||||||
reset: '重置',
|
reset: '重置',
|
||||||
submit: '保存',
|
submit: '保存',
|
||||||
|
limit: '限制',
|
||||||
noData: '没有数据',
|
noData: '没有数据',
|
||||||
tag: '标签',
|
tag: '标签',
|
||||||
syncChart: '同步图表',
|
syncChart: '同步图表',
|
||||||
@@ -519,6 +522,10 @@ const cn = {
|
|||||||
down: 'down',
|
down: 'down',
|
||||||
prometheus: 'prometheus'
|
prometheus: 'prometheus'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
explore: {
|
||||||
|
descending: '降序',
|
||||||
|
wrapLines: '换行'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
validate: {
|
validate: {
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ const en = {
|
|||||||
oneDay: '1 day',
|
oneDay: '1 day',
|
||||||
twoDay: '2 days',
|
twoDay: '2 days',
|
||||||
week: '1 week',
|
week: '1 week',
|
||||||
|
time: 'Time',
|
||||||
folder: 'Folder',
|
folder: 'Folder',
|
||||||
key: 'Key',
|
key: 'Key',
|
||||||
|
logs: 'Logs',
|
||||||
state: 'State',
|
state: 'State',
|
||||||
projectName: 'Project name',
|
projectName: 'Project name',
|
||||||
startTime: 'Start time',
|
startTime: 'Start time',
|
||||||
@@ -69,6 +71,7 @@ const en = {
|
|||||||
about: 'About', // 关于
|
about: 'About', // 关于
|
||||||
detail: 'Detail', // 详情
|
detail: 'Detail', // 详情
|
||||||
query: 'Query', // 查询
|
query: 'Query', // 查询
|
||||||
|
logLabels: 'Log labels',
|
||||||
account: 'Account',
|
account: 'Account',
|
||||||
back: 'Back', // 返回
|
back: 'Back', // 返回
|
||||||
unavailable: 'Unavailable',
|
unavailable: 'Unavailable',
|
||||||
@@ -118,6 +121,7 @@ const en = {
|
|||||||
},
|
},
|
||||||
reset: 'Reset',
|
reset: 'Reset',
|
||||||
submit: 'Save',
|
submit: 'Save',
|
||||||
|
limit: 'Limit',
|
||||||
noData: 'No data',
|
noData: 'No data',
|
||||||
tag: 'Tag',
|
tag: 'Tag',
|
||||||
placeHolder: 'Please enter',
|
placeHolder: 'Please enter',
|
||||||
@@ -528,6 +532,10 @@ const en = {
|
|||||||
down: 'down',
|
down: 'down',
|
||||||
prometheus: 'prometheus'
|
prometheus: 'prometheus'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
explore: {
|
||||||
|
descending: 'Descending',
|
||||||
|
wrapLines: 'Wrap lines'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
validate: { // 校验规则
|
validate: { // 校验规则
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="interval-refresh">
|
<div class="interval-refresh">
|
||||||
<time-picker v-if="showTimePicker" ref="timePicker" v-model="searchTime" :default-pick="defaultPick" :show-empty="showEmpty" class="time-picker margin-r-10" size="small" @change="dateChange"></time-picker>
|
<time-picker v-if="showTimePicker" ref="timePicker" v-model="searchTime" :default-pick="defaultPick" :show-empty="showEmpty" class="time-picker margin-r-10" size="small" @change="dateChange"></time-picker>
|
||||||
<multipleTime ref="multipleTime" v-if="showMultiple" :stepSearchTime="searchTime" @change="dateChange(searchTime)" class="multiple-time"/>
|
<multipleTime v-if="showMultiple" ref="multipleTime" :stepSearchTime="searchTime" class="multiple-time margin-r-10" @change="dateChange(searchTime)"/>
|
||||||
<chart-unit v-model="unit" v-if="useChartUnit" style="margin-left: 10px"></chart-unit>
|
<chart-unit v-if="useChartUnit" v-model="unit" class="margin-r-10"></chart-unit>
|
||||||
<div v-show="useRefresh" class="top-tool-btn-group margin-r-10">
|
<div v-show="useRefresh" class="top-tool-btn-group">
|
||||||
<button :id="id+'-refresh'" class="top-tool-btn top-tool-btn--text" @click="refreshDataFunc">
|
<button :id="id+'-refresh'" class="top-tool-btn top-tool-btn--text" @click="refreshDataFunc">
|
||||||
<i class="global-active-color nz-icon nz-icon-refresh" style="font-size: 14px"></i>
|
<i class="global-active-color nz-icon nz-icon-refresh" style="font-size: 14px"></i>
|
||||||
<span><slot name="added-text"></slot></span>
|
<span><slot name="added-text"></slot></span>
|
||||||
|
|||||||
@@ -1,677 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="explore list-page">
|
<div class="explores">
|
||||||
<div class="main-list">
|
<explore-item
|
||||||
<div class="main-container" style="overflow: auto;">
|
v-for="item in exploreItems"
|
||||||
<!-- 顶部工具栏 -->
|
:key="item"
|
||||||
<div class="top-tools" style="z-index: 1">
|
:closable="closable"
|
||||||
<div class="top-tool-left"></div>
|
:tab-index="item"
|
||||||
<div class="top-tool-right">
|
@split="split"
|
||||||
<pick-time :refresh-data-func="expressionChange" v-model="filterTime" @unitChange="chartUnitChange" ref="pickTime" id="explore">
|
>
|
||||||
<template slot="added-text">{{$t('dashboard.metricPreview.runQuery')}}</template>
|
</explore-item>
|
||||||
</pick-time>
|
|
||||||
<button id="explore-save-chart" v-has="'panel_chart_add'" :disabled="saveDisabled" class="top-tool-btn top-tool-btn--text" type="button"
|
|
||||||
@click="saveChart"
|
|
||||||
:class="{'nz-btn-disabled btn-disabled-cursor-not-allowed' : saveDisabled}">
|
|
||||||
{{$t('dashboard.metric.saveChart')}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="explore-promql-box" class="top-tools" style="padding-top: 0; flex-wrap: wrap">
|
|
||||||
<!--坑,这个index居然是从1开始-->
|
|
||||||
<promql-input
|
|
||||||
v-for="index of promqlKeys.length"
|
|
||||||
:id="promqlKeys[index-1]"
|
|
||||||
:key="promqlKeys[index-1]"
|
|
||||||
:ref="'promql-'+(index-1)"
|
|
||||||
:expression-list="expressions"
|
|
||||||
:index="index-1"
|
|
||||||
:plugins="['metric-selector', 'metric-input', 'add', 'remove', 'copy']"
|
|
||||||
:styleType="1"
|
|
||||||
@addExpression="addExpression"
|
|
||||||
@copyExpression="copyExpression"
|
|
||||||
@removeExpression="removeExpression"
|
|
||||||
></promql-input>
|
|
||||||
</div>
|
|
||||||
<div ref="scrollWrap" style="height: auto; padding: 0 20px 4px;">
|
|
||||||
<div v-show="!showIntroduce" class="chart-view"
|
|
||||||
:class="{'shrink-view':!chartVisible || !defaultChartVisible}" style="position:relative;">
|
|
||||||
<div class="view-title" @click="changeChartVisible" style="position:absolute;z-index: 1000;top:10px;"><i class="nz-icon nz-icon-caret-top" ></i> graph</div>
|
|
||||||
<div class="chart-room">
|
|
||||||
<chart ref="exploreChart" :unit="chartUnit"></chart>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-show="!showIntroduce" class="table-view"
|
|
||||||
:class="{'shrink-view':!tableVisible || !defaultTableVisible}" style="position: relative">
|
|
||||||
<div class="view-title" style="position:absolute;z-index: 1000;top:4px;" @click="changeTableVisible"><i class="nz-icon nz-icon-caret-top"></i> table</div>
|
|
||||||
<div class="table-room">
|
|
||||||
<!-- 自定义table列 -->
|
|
||||||
<transition name="el-zoom-in-top">
|
|
||||||
<element-set
|
|
||||||
class="pop-custom-explore"
|
|
||||||
v-if="tools.showCustomTableTitle"
|
|
||||||
@close="tools.showCustomTableTitle = false"
|
|
||||||
:custom-table-title.sync="tools.customTableTitle"
|
|
||||||
:original-table-title="tableTitle"
|
|
||||||
ref="customTableTitle"
|
|
||||||
></element-set>
|
|
||||||
</transition>
|
|
||||||
<el-table class="nz-table explore-table"
|
|
||||||
:data="tableData"
|
|
||||||
border
|
|
||||||
ref="exploreTable"
|
|
||||||
tooltip-effect="light"
|
|
||||||
v-loading="tools.loading">
|
|
||||||
<el-table-column
|
|
||||||
:resizable="false"
|
|
||||||
v-for="(item, index) in tools.customTableTitle"
|
|
||||||
v-if="item.show"
|
|
||||||
:key="`col-${index}`"
|
|
||||||
:label="item.label"
|
|
||||||
:prop="item.prop"
|
|
||||||
show-overflow-tooltip
|
|
||||||
min-width="110px"
|
|
||||||
></el-table-column>
|
|
||||||
<el-table-column width="28" v-if="tools.customTableTitle.length>0">
|
|
||||||
<template slot="header" :resizable="false">
|
|
||||||
<span @mousedown.stop="!tools.showCustomTableTitle && (tools.showCustomTableTitle = true)" class="nz-table-gear">
|
|
||||||
<i class="nz-icon nz-icon-gear"></i>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<pagination :page-obj="pageObj" @pageNo='pageNo' @pageSize='pageSize' ref="Pagination"
|
|
||||||
:append-to-body="false"></pagination>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-show="showIntroduce" class="introduce-view">
|
|
||||||
<div class="info-room">
|
|
||||||
<div class="col-md-9 doc-content">
|
|
||||||
<h1 class="page-header">Query examples<a class="header-anchor" href="https://prometheus.io/docs/prometheus/latest/querying/examples/" target="_blank" rel="noopener noreferrer"><i style="font-size: 16px;" class="nz-icon nz-icon-link1"></i></a></h1>
|
|
||||||
<div class="content-divider"></div>
|
|
||||||
<h2 >
|
|
||||||
Simple time series selection
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<p>Return all time series with the metric <code>http_requests_total</code>:</p>
|
|
||||||
|
|
||||||
<pre><code>http_requests_total</code></pre>
|
|
||||||
|
|
||||||
<p>Return all time series with the metric <code>http_requests_total</code> and the given<code>job</code> and <code>handler</code> labels:</p>
|
|
||||||
|
|
||||||
<pre><code>http_requests_total{job="apiserver", handler="/api/comments"}</code></pre>
|
|
||||||
|
|
||||||
<p>Return a whole range of time (in this case 5 minutes) for the same vector,
|
|
||||||
making it a range vector:</p>
|
|
||||||
|
|
||||||
<pre><code>http_requests_total{job="apiserver", handler="/api/comments"}[5m]</code></pre>
|
|
||||||
|
|
||||||
<p>Note that an expression resulting in a range vector cannot be graphed directly,
|
|
||||||
but viewed in the tabular ("Console") view of the expression browser.</p>
|
|
||||||
|
|
||||||
<p>Using regular expressions, you could select time series only for jobs whose
|
|
||||||
name match a certain pattern, in this case, all jobs that end with <code>server</code>:</p>
|
|
||||||
|
|
||||||
<pre><code>http_requests_total{job=~".*server"}</code></pre>
|
|
||||||
|
|
||||||
<p>All regular expressions in Prometheus use RE2 syntax.</p>
|
|
||||||
|
|
||||||
<p>To select all HTTP status codes except 4xx ones, you could run:</p>
|
|
||||||
|
|
||||||
<pre><code>http_requests_total{status!~"4.."}</code></pre>
|
|
||||||
|
|
||||||
<h2 >
|
|
||||||
Subquery
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<p>Return the 5-minute rate of the <code>http_requests_total</code> metric for the past 30 minutes, with a resolution of 1 minute.</p>
|
|
||||||
|
|
||||||
<pre><code>rate(http_requests_total[5m])[30m:1m]</code></pre>
|
|
||||||
|
|
||||||
<p>This is an example of a nested subquery. The subquery for the <code>deriv</code> function uses the default resolution. Note that using subqueries unnecessarily is unwise.</p>
|
|
||||||
|
|
||||||
<pre><code>max_over_time(deriv(rate(distance_covered_total[5s])[30s:5s])[10m:])</code></pre>
|
|
||||||
|
|
||||||
<h2 >
|
|
||||||
Using functions, operators, etc.
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<p>Return the per-second rate for all time series with the <code>http_requests_total</code>
|
|
||||||
metric name, as measured over the last 5 minutes:</p>
|
|
||||||
|
|
||||||
<pre><code>rate(http_requests_total[5m])</code></pre>
|
|
||||||
|
|
||||||
<p>Assuming that the <code>http_requests_total</code> time series all have the labels <code>job</code>
|
|
||||||
(fanout by job name) and <code>instance</code> (fanout by instance of the job), we might
|
|
||||||
want to sum over the rate of all instances, so we get fewer output time series,
|
|
||||||
but still preserve the <code>job</code> dimension:</p>
|
|
||||||
|
|
||||||
<pre><code>sum by (job) (rate(http_requests_total[5m]))</code></pre>
|
|
||||||
|
|
||||||
<p>If we have two different metrics with the same dimensional labels, we can apply
|
|
||||||
binary operators to them and elements on both sides with the same label set
|
|
||||||
will get matched and propagated to the output. For example, this expression
|
|
||||||
returns the unused memory in MiB for every instance (on a fictional cluster
|
|
||||||
scheduler exposing these metrics about the instances it runs):</p>
|
|
||||||
|
|
||||||
<pre><code>(instance_memory_limit_bytes - instance_memory_usage_bytes) / 1024 / 1024</code></pre>
|
|
||||||
|
|
||||||
<p>The same expression, but summed by application, could be written like this:</p>
|
|
||||||
|
|
||||||
<pre><code>sum by (app, proc) (instance_memory_limit_bytes - instance_memory_usage_bytes) / 1024 / 1024</code></pre>
|
|
||||||
|
|
||||||
<p>If the same fictional cluster scheduler exposed CPU usage metrics like the following for every instance:</p>
|
|
||||||
|
|
||||||
<pre><code>instance_cpu_time_ns{app="lion", proc="web", rev="34d0f99", env="prod", job="cluster-manager"}
|
|
||||||
instance_cpu_time_ns{app="elephant", proc="worker", rev="34d0f99", env="prod", job="cluster-manager"}
|
|
||||||
instance_cpu_time_ns{app="turtle", proc="api", rev="4d3a513", env="prod", job="cluster-manager"}
|
|
||||||
instance_cpu_time_ns{app="fox", proc="widget", rev="4d3a513", env="prod", job="cluster-manager"}
|
|
||||||
...
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<p>...we could get the top 3 CPU users grouped by application (<code>app</code>) and process type (<code>proc</code>) like this:</p>
|
|
||||||
|
|
||||||
<pre><code>topk(3, sum by (app, proc) (rate(instance_cpu_time_ns[5m])))</code></pre>
|
|
||||||
|
|
||||||
<p>Assuming this metric contains one time series per running instance, you could count the number of running instances per application like this:</p>
|
|
||||||
|
|
||||||
<pre><code>count by (app) (instance_cpu_time_ns)</code></pre>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<transition name="right-box">
|
|
||||||
<chart-box :chart="chart" :from="$CONSTANTS.fromRoute.explore" :panel-data="panelData" :show-panel="{id: -1, name: '', type: 'explore'}" @close="handleBox(false)" @on-create-success="createSuccess" ref="addChartModal" v-if="rightBox.show"></chart-box>
|
|
||||||
</transition>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import bus from '../../../../libs/bus'
|
import exploreItem from '@/components/page/dashboard/explore/exploreItem'
|
||||||
import promqlInput from './promqlInput'
|
|
||||||
// import promqlInputPlus from "./promqlInputPlus";
|
|
||||||
import chart from '../overview/chart'
|
|
||||||
import axios from 'axios'
|
|
||||||
import chartBox from '../../../page/dashboard/chartBox'
|
|
||||||
import { getUUID } from '../../../common/js/common'
|
|
||||||
import chartDataFormat from '../../../charts/chartDataFormat'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'explore',
|
name: 'explore',
|
||||||
components: {
|
components: {
|
||||||
'promql-input': promqlInput,
|
exploreItem
|
||||||
chart: chart,
|
|
||||||
'chart-box': chartBox
|
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
rightBox: { // 面板弹出框相关
|
exploreItems: [0]
|
||||||
show: false
|
|
||||||
},
|
|
||||||
promqlCount: 1,
|
|
||||||
promqlKeys: [],
|
|
||||||
expressions: [''],
|
|
||||||
filterTime: [
|
|
||||||
bus.timeFormate(bus.getOffsetTimezoneData(-1), 'yyyy-MM-dd hh:mm:ss'),
|
|
||||||
bus.timeFormate(bus.getOffsetTimezoneData(), 'yyyy-MM-dd hh:mm:ss')
|
|
||||||
],
|
|
||||||
|
|
||||||
/* 工具参数 */
|
|
||||||
tools: {
|
|
||||||
loading: false, // 是否显示table加载动画
|
|
||||||
showCustomTableTitle: false, // 自定义列弹框是否显示
|
|
||||||
customTableTitle: [] // 自定义列工具的数据
|
|
||||||
},
|
|
||||||
tableTitle: [],
|
|
||||||
showIntroduce: true,
|
|
||||||
defaultChartVisible: true,
|
|
||||||
defaultTableVisible: true,
|
|
||||||
chartVisible: true,
|
|
||||||
tableVisible: true,
|
|
||||||
pageObj: {
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: this.$CONSTANTS.defaultPageSize,
|
|
||||||
total: 0
|
|
||||||
},
|
|
||||||
tableData: [],
|
|
||||||
saveDisabled: true,
|
|
||||||
panelData: [],
|
|
||||||
chartUnit: 0,
|
|
||||||
historyParam: { useHistory: true, key: 'expore-history' },
|
|
||||||
chart: {},
|
|
||||||
metricOptions: []
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
computed: {
|
||||||
this.getPanelData()
|
closable () {
|
||||||
this.queryMetrics()
|
return this.exploreItems.length === 2
|
||||||
this.promqlKeys.push(getUUID())
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
pageNo (val) {
|
split (index) {
|
||||||
this.pageObj.pageNo = val
|
const tabIndex = this.exploreItems.indexOf(index)
|
||||||
this.tableData = this.filterShowData(this.storedTableData, this.pageObj)
|
if (tabIndex > -1) {
|
||||||
},
|
if (this.exploreItems.length === 2) {
|
||||||
pageSize (val) {
|
this.exploreItems.splice(tabIndex, 1)
|
||||||
this.pageObj.pageSize = val
|
|
||||||
this.tableData = this.filterShowData(this.storedTableData, this.pageObj)
|
|
||||||
},
|
|
||||||
filterShowData (source, pageObj) {
|
|
||||||
return source.slice((pageObj.pageNo - 1) * pageObj.pageSize, pageObj.pageNo * pageObj.pageSize)
|
|
||||||
},
|
|
||||||
chartUnitChange: function (unit) {
|
|
||||||
this.chartUnit = unit
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.expressionChange()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
queryChartData: function () {
|
|
||||||
this.$refs.exploreChart.startLoading()
|
|
||||||
setTimeout(() => {
|
|
||||||
if (this.expressions.length > 0) {
|
|
||||||
const requestArr = []
|
|
||||||
const promqlInputIndexs = []
|
|
||||||
const queryExpression = []
|
|
||||||
this.expressions.forEach((item, index) => {
|
|
||||||
if (item != '') {
|
|
||||||
const step = bus.getStep(this.filterTime[0], this.filterTime[1])
|
|
||||||
promqlInputIndexs.push(index)
|
|
||||||
queryExpression.push(item)
|
|
||||||
requestArr.push(this.$get('/prom/api/v1/query_range?query=' + item + '&start=' + this.$stringTimeParseToUnix(this.filterTime[0]) + '&end=' + this.$stringTimeParseToUnix(this.filterTime[1]) + '&step=' + step))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (requestArr.length > 0) {
|
|
||||||
this.showIntroduce = false
|
|
||||||
this.saveDisabled = false
|
|
||||||
}
|
|
||||||
axios.all(requestArr).then(res => {
|
|
||||||
const series = []
|
|
||||||
const legend = []
|
|
||||||
if (res.length > 0) {
|
|
||||||
res.forEach((response, index) => {
|
|
||||||
const promqlIndex = promqlInputIndexs[index]
|
|
||||||
if (response.data && response.status == 'success') {
|
|
||||||
const data = response.data.result
|
|
||||||
if ((!data || data.length < 1) && response.message) {
|
|
||||||
this.$refs['promql-' + promqlIndex][0].setError(response.message)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data.forEach((result, i) => {
|
|
||||||
const seriesItem = {
|
|
||||||
name: '',
|
|
||||||
symbol: 'emptyCircle', // 去掉点
|
|
||||||
symbolSize: [2, 2],
|
|
||||||
showSymbol: false,
|
|
||||||
smooth: 0.2, // 曲线变平滑
|
|
||||||
data: [],
|
|
||||||
lineStyle: {
|
|
||||||
width: 1,
|
|
||||||
opacity: 0.9
|
|
||||||
},
|
|
||||||
type: 'line'
|
|
||||||
}
|
|
||||||
let legendName = ''
|
|
||||||
seriesItem.data = result.values.map((item) => {
|
|
||||||
return [item[0] * 1000, item[1]]
|
|
||||||
})
|
|
||||||
if (result.metric && Object.keys(result.metric).length > 0) {
|
|
||||||
const metric = Object.assign({}, result.metric)
|
|
||||||
seriesItem.name += metric.__name__ ? metric.__name__ : ''
|
|
||||||
seriesItem.name += '{'
|
|
||||||
delete metric.__name__
|
|
||||||
for (const key in metric) {
|
|
||||||
seriesItem.name += key + '=' + '"' + metric[key] + '",'
|
|
||||||
}
|
|
||||||
legendName = seriesItem.name.substr(0, seriesItem.name.length - 1)
|
|
||||||
legendName += '}'
|
|
||||||
} else {
|
} else {
|
||||||
legendName = queryExpression[index]
|
this.exploreItems.push(index === 0 ? 1 : 0)
|
||||||
}
|
|
||||||
seriesItem.name = legendName + '-' + index
|
|
||||||
series.push(seriesItem)
|
|
||||||
legend.push({ name: seriesItem.name, alias: legendName, isGray: false })
|
|
||||||
})
|
|
||||||
|
|
||||||
this.$refs['promql-' + promqlIndex][0].setError('')
|
|
||||||
} else {
|
|
||||||
// console.log(response)
|
|
||||||
this.$refs['promql-' + promqlIndex][0].setError(response.error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
this.$refs.exploreChart.setLegend(legend)
|
|
||||||
this.$refs.exploreChart.setRandomColors(series.length)
|
|
||||||
this.$refs.exploreChart.setSeries(series)
|
|
||||||
this.defaultChartVisible = true
|
|
||||||
}
|
|
||||||
this.$refs.exploreChart.endLoading()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, 200)
|
|
||||||
},
|
|
||||||
queryTableData () {
|
|
||||||
this.tools.loading = true
|
|
||||||
setTimeout(() => {
|
|
||||||
if (this.expressions.length > 0) {
|
|
||||||
const requestArr = []
|
|
||||||
this.expressions.forEach((item, index) => {
|
|
||||||
if (item !== '') {
|
|
||||||
requestArr.push(this.$get('/prom/api/v1/query?query=' + item))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (requestArr.length > 0) {
|
|
||||||
this.showIntroduce = false
|
|
||||||
}
|
|
||||||
axios.all(requestArr).then(res => {
|
|
||||||
const tData = []
|
|
||||||
const tLabels = []
|
|
||||||
if (res.length > 0) {
|
|
||||||
this.tableData = []
|
|
||||||
this.tableTitle = []
|
|
||||||
res.forEach((response, index) => {
|
|
||||||
if (response.data && response.status === 'success') {
|
|
||||||
const data = response.data.result
|
|
||||||
if (data) {
|
|
||||||
data.forEach((result, i) => {
|
|
||||||
const metrics = Object.assign({}, result.metric)
|
|
||||||
if (!Array.isArray(result.value)) {
|
|
||||||
const val = result
|
|
||||||
result = {
|
|
||||||
value: ['', val]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.$set(metrics, 'value#' + index, chartDataFormat.getUnit(this.chartUnit).compute(result.value[1], null, 2))
|
|
||||||
for (const key in metrics) {
|
|
||||||
const label = {
|
|
||||||
label: key === '__name__' ? 'metric' : key,
|
|
||||||
prop: key,
|
|
||||||
show: true
|
|
||||||
}
|
|
||||||
const temp = tLabels.find((item, index) => {
|
|
||||||
return item.prop == label.prop
|
|
||||||
})
|
|
||||||
if (!temp) {
|
|
||||||
tLabels.push(label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tData.push(metrics)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tLabels.sort((a, b) => {
|
|
||||||
return a.prop.charCodeAt(0) - b.prop.charCodeAt(0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if (tData.length > 0) {
|
|
||||||
this.storedTableData = Object.assign([], tData)
|
|
||||||
this.pageObj.total = this.storedTableData.length
|
|
||||||
this.tableData = this.filterShowData(this.storedTableData, this.pageObj)
|
|
||||||
this.tableTitle = Object.assign([], tLabels)
|
|
||||||
this.tools.customTableTitle = Object.assign([], tLabels)
|
|
||||||
this.defaultTableVisible = true
|
|
||||||
} else {
|
|
||||||
// this.defaultTableVisible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.tools.loading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, 200)
|
|
||||||
},
|
|
||||||
expressionChange: function () {
|
|
||||||
const nowTimeType = this.$refs.pickTime.$refs.timePicker.nowTimeType
|
|
||||||
// console.log(this.filterTime,nowTimeType);
|
|
||||||
this.setSearchTime(nowTimeType.type, nowTimeType.value)
|
|
||||||
if (this.expressions && this.expressions.length >= 1) {
|
|
||||||
this.queryTableData()
|
|
||||||
this.queryChartData()
|
|
||||||
this.storeHistory()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setSearchTime (type, val) { // 设置searchTime
|
|
||||||
if (type === 'minute') {
|
|
||||||
const startTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())).setMinutes(new Date(bus.computeTimezone(new Date().getTime())).getMinutes() - val), 'yyyy-MM-dd hh:mm:ss')
|
|
||||||
const endTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())), 'yyyy-MM-dd hh:mm:ss')
|
|
||||||
this.$set(this.filterTime, 0, startTime)
|
|
||||||
this.$set(this.filterTime, 1, endTime)
|
|
||||||
this.$set(this.filterTime, 2, val + 'm')
|
|
||||||
} else if (type === 'hour') {
|
|
||||||
const startTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())).setHours(new Date(bus.computeTimezone(new Date().getTime())).getHours() - val), 'yyyy-MM-dd hh:mm:ss')
|
|
||||||
const endTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())), 'yyyy-MM-dd hh:mm:ss')
|
|
||||||
this.$set(this.filterTime, 0, startTime)
|
|
||||||
this.$set(this.filterTime, 1, endTime)
|
|
||||||
this.$set(this.filterTime, 2, val + 'h')
|
|
||||||
} else if (type === 'date') {
|
|
||||||
const startTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())).setDate(new Date(bus.computeTimezone(new Date().getTime())).getDate() - val), 'yyyy-MM-dd hh:mm:ss')
|
|
||||||
const endTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())), 'yyyy-MM-dd hh:mm:ss')
|
|
||||||
this.$set(this.filterTime, 0, startTime)
|
|
||||||
this.$set(this.filterTime, 1, endTime)
|
|
||||||
this.$set(this.filterTime, 2, val + 'd')
|
|
||||||
}
|
|
||||||
this.$refs.pickTime.$refs.timePicker.searchTime = this.filterTime
|
|
||||||
},
|
|
||||||
storeHistory: function () {
|
|
||||||
const expire = 24
|
|
||||||
const historyJson = localStorage.getItem(this.historyParam.key)
|
|
||||||
const expressions = this.expressions.filter(item => {
|
|
||||||
return item && item != ''
|
|
||||||
})
|
|
||||||
const username = sessionStorage.getItem('nz-username')
|
|
||||||
if (historyJson && historyJson != 'undefined' && historyJson != '') {
|
|
||||||
const historyObj = JSON.parse(historyJson)
|
|
||||||
let history = historyObj[username]
|
|
||||||
if (history) {
|
|
||||||
// 过滤过期表达式
|
|
||||||
history = history.filter(item => {
|
|
||||||
return item.time + item.expire >= new Date().getTime()
|
|
||||||
})
|
|
||||||
let repeat = history.filter(item => {
|
|
||||||
return expressions.includes(item.insertText)
|
|
||||||
})
|
|
||||||
const old = history.filter(item => {
|
|
||||||
return !expressions.includes(item.insertText)
|
|
||||||
})
|
|
||||||
|
|
||||||
const freshExpression = expressions.filter(item => {
|
|
||||||
const find = history.find(t => { return t.insertText == item })
|
|
||||||
return !find
|
|
||||||
})
|
|
||||||
|
|
||||||
repeat = repeat.map(item => {
|
|
||||||
item.time = new Date().getTime()
|
|
||||||
item.num += 1
|
|
||||||
item.documentation = this.$t('dashboard.metricPreview.historyTip', { time: item.num, hour: 24 })
|
|
||||||
return item
|
|
||||||
})
|
|
||||||
|
|
||||||
const fresh = freshExpression.map(item => {
|
|
||||||
return {
|
|
||||||
label: item,
|
|
||||||
insertText: item,
|
|
||||||
documentation: this.$t('dashboard.metricPreview.historyTip', { time: 1, hour: 24 }),
|
|
||||||
num: 1,
|
|
||||||
time: new Date().getTime(),
|
|
||||||
expire: expire * 60 * 60 * 1000
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
historyObj[username] = fresh.concat(repeat).concat(old)
|
|
||||||
} else {
|
|
||||||
const history = expressions.map(item => {
|
|
||||||
return {
|
|
||||||
label: item,
|
|
||||||
insertText: item,
|
|
||||||
documentation: this.$t('dashboard.metricPreview.historyTip', { time: 1, hour: 24 }),
|
|
||||||
num: 1,
|
|
||||||
time: new Date().getTime(),
|
|
||||||
expire: expire * 60 * 60 * 1000
|
|
||||||
}
|
|
||||||
})
|
|
||||||
historyObj[username] = history
|
|
||||||
}
|
|
||||||
localStorage.setItem(this.historyParam.key, JSON.stringify(historyObj))
|
|
||||||
} else {
|
|
||||||
const history = expressions.map(item => {
|
|
||||||
return {
|
|
||||||
label: item,
|
|
||||||
insertText: item,
|
|
||||||
documentation: this.$t('dashboard.metricPreview.historyTip', { time: 1, hour: 24 }),
|
|
||||||
num: 1,
|
|
||||||
time: new Date().getTime(),
|
|
||||||
expire: expire * 60 * 60 * 1000
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (history && history.length > 0) {
|
|
||||||
const stored = {}
|
|
||||||
stored[username] = history
|
|
||||||
localStorage.setItem(this.historyParam.key, JSON.stringify(stored))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
addExpression (index) {
|
|
||||||
this.expressions.splice(index + 1, 0, '')
|
|
||||||
this.promqlKeys.splice(index + 1, 0, getUUID())
|
|
||||||
this.promqlCount++
|
|
||||||
},
|
|
||||||
copyExpression (index) {
|
|
||||||
this.expressions.push(this.expressions[index])
|
|
||||||
this.promqlKeys.push(getUUID())
|
|
||||||
this.promqlCount++
|
|
||||||
},
|
|
||||||
removeExpression (index) {
|
|
||||||
if (this.promqlCount > 1) {
|
|
||||||
this.expressions.splice(index, 1)
|
|
||||||
this.promqlKeys.splice(index, 1)
|
|
||||||
this.promqlCount--
|
|
||||||
}
|
|
||||||
},
|
|
||||||
changeChartVisible: function () {
|
|
||||||
this.chartVisible = !this.chartVisible
|
|
||||||
},
|
|
||||||
changeTableVisible: function () {
|
|
||||||
this.tableVisible = !this.tableVisible
|
|
||||||
},
|
|
||||||
handleBox (show) {
|
|
||||||
this.rightBox.show = show
|
|
||||||
},
|
|
||||||
saveChart () {
|
|
||||||
const chart = {
|
|
||||||
name: '',
|
|
||||||
type: 'line',
|
|
||||||
span: 12,
|
|
||||||
height: '400',
|
|
||||||
unit: this.chartUnit,
|
|
||||||
param: {
|
|
||||||
url: '',
|
|
||||||
threshold: ''
|
|
||||||
},
|
|
||||||
elements: [],
|
|
||||||
panel: '',
|
|
||||||
sync: 0,
|
|
||||||
groupId: '',
|
|
||||||
remark: ''
|
|
||||||
}
|
|
||||||
this.expressions.forEach((exp) => {
|
|
||||||
chart.elements.push({ expression: exp, legend: '', type: 'expert', id: '' })
|
|
||||||
})
|
|
||||||
this.chart = chart
|
|
||||||
this.rightBox.show = true
|
|
||||||
},
|
|
||||||
createSuccess (type, response, param, panel) { // 添加chart成功
|
|
||||||
this.$confirm(this.$t('dashboard.metric.goPanelTip'), this.$t('tip.saveSuccess'), {
|
|
||||||
confirmButtonText: this.$t('tip.yes'),
|
|
||||||
cancelButtonText: this.$t('tip.no'),
|
|
||||||
type: 'success'
|
|
||||||
}).then(() => {
|
|
||||||
bus.$emit('menu-change', 'panel')
|
|
||||||
this.$store.commit('panelShowPanelChange', panel)
|
|
||||||
this.$router.push({
|
|
||||||
path: '/panel',
|
|
||||||
query: {
|
|
||||||
t: +new Date()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getPanelData () { // 获取panel数据
|
|
||||||
this.$get('visual/panel?pageNo=1&pageSize=-1').then(response => {
|
|
||||||
if (response.code === 200) {
|
|
||||||
this.panelData = response.data.list
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
jumpTo (data, id) {
|
|
||||||
bus.$emit('menu-change', data)
|
|
||||||
this.$router.push({
|
|
||||||
path: '/' + data,
|
|
||||||
query: {
|
|
||||||
t: +new Date()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
queryMetrics () {
|
|
||||||
this.metricOptions = []
|
|
||||||
this.$get('prom/api/v1/label/__name__/values').then(response => {
|
|
||||||
if (response.status == 'success') {
|
|
||||||
const metrics = response.data.sort()
|
|
||||||
const metricMap = new Map()
|
|
||||||
metrics.forEach((item) => {
|
|
||||||
let key = ''
|
|
||||||
if (/^[a-zA-Z]+?_[a-zA-Z]*/.test(item)) {
|
|
||||||
key = item.split('_')[0]
|
|
||||||
} else if (/^_\w*/.test(item)) {
|
|
||||||
key = ' '
|
|
||||||
} else {
|
|
||||||
key = item
|
|
||||||
}
|
|
||||||
if (metricMap.get(key)) {
|
|
||||||
const values = metricMap.get(key)
|
|
||||||
values.push({ label: item, value: item })
|
|
||||||
} else {
|
|
||||||
const values = [{ label: item, value: item }]
|
|
||||||
metricMap.set(key, values)
|
|
||||||
}
|
|
||||||
// this.metricStore.push({label:item,value:item,insertText:item})
|
|
||||||
})
|
|
||||||
for (const key of metricMap.keys()) {
|
|
||||||
const option = {
|
|
||||||
label: key,
|
|
||||||
value: key
|
|
||||||
}
|
|
||||||
if (metricMap.get(key) && metricMap.get(key).length > 1) {
|
|
||||||
option.children = metricMap.get(key)
|
|
||||||
}
|
|
||||||
this.metricOptions.push(option)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getMetricOptions () {
|
|
||||||
return this.metricOptions
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
promqlCount: function (n, o) {
|
|
||||||
this.expressionChange()
|
|
||||||
},
|
|
||||||
expressions: {
|
|
||||||
immediate: true,
|
|
||||||
handler: function (n, o) {
|
|
||||||
if (n.length == 1 && (!n[0] || n[0] == '')) {
|
|
||||||
this.showIntroduce = true
|
|
||||||
} else if (n.length > 1) {
|
|
||||||
const temp = n.find((item, index) => {
|
|
||||||
return item != ''
|
|
||||||
})
|
|
||||||
if (!temp) {
|
|
||||||
this.showIntroduce = true
|
|
||||||
} else {
|
|
||||||
this.showIntroduce = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.showIntroduce = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -679,248 +43,11 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped>
|
||||||
.explore {
|
.explores {
|
||||||
height: 100%;
|
display: flex;
|
||||||
}
|
padding: 10px 0 10px 10px;
|
||||||
|
background-color: #f6f6f6;
|
||||||
.explore .chart-room {
|
box-sizing: border-box;
|
||||||
width: 100%;
|
|
||||||
height: 400px
|
|
||||||
}
|
|
||||||
|
|
||||||
.explore .chart-view, .table-view {
|
|
||||||
padding: 22px 10px 10px 10px;
|
|
||||||
border: 1px solid lightgrey;
|
|
||||||
box-sizing: inherit;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
transition: height 1s;
|
|
||||||
}
|
|
||||||
.explore .chart-view:hover,.explore .table-view:hover {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
.shrink-view {
|
|
||||||
height: 30px;
|
|
||||||
|
|
||||||
.view-title i {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-room, .table-room {
|
|
||||||
height: 0px;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
.table-room {
|
|
||||||
position: relative;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
.explore .view-title {
|
|
||||||
font-weight: 500;
|
|
||||||
margin-right: 8px;
|
|
||||||
font-size: 14px;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.introduce-view .info-room {
|
|
||||||
padding: 24px;
|
|
||||||
background-color: #e9edf2;
|
|
||||||
border-top: 3px solid #3274d9;
|
|
||||||
-webkit-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%, .1), 1px 1px 0 0 rgba(0, 0, 0, .1);
|
|
||||||
-webkit-box-flex: 1;
|
|
||||||
-webkit-flex-grow: 1;
|
|
||||||
-ms-flex-positive: 1;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-room .cheat-sheet-item__title {
|
|
||||||
font-size: 21px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-room .cheat-sheet-item__label {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-room code {
|
|
||||||
font-family: Menlo, Monaco, Consolas, Courier New, monospace;
|
|
||||||
font-size: 11px;
|
|
||||||
background-color: #e9edf2;
|
|
||||||
color: #52545c;
|
|
||||||
border: 1px solid #c7d0d9;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
/*外部引用 样式start*/
|
|
||||||
.doc-content {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content p, .doc-content.ul, .doc-content .alert {
|
|
||||||
margin: 15px 0 15px 0;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content .content-divider{
|
|
||||||
height: 1px;
|
|
||||||
width:100%;
|
|
||||||
border-bottom: 2px solid #C0C4CC;
|
|
||||||
margin: 5px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content > h1 {
|
|
||||||
color: #e6522c;
|
|
||||||
font-size: 30px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content > h1 a {
|
|
||||||
color: #000 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content.blog > h1 {
|
|
||||||
text-transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content.blog .sponsor-logos > a > img {
|
|
||||||
width: 250px;
|
|
||||||
display: inline-block !important;
|
|
||||||
margin: 15px 55px;
|
|
||||||
}
|
|
||||||
.doc-content > h1 {
|
|
||||||
color: #e6522c;
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content > h2 {
|
|
||||||
color: #e6522c;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content > h2 code {
|
|
||||||
color: #e6522c;
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content > h3 {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content > h4 {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 18px;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content a.header-anchor {
|
|
||||||
padding-left: 15px;
|
|
||||||
color: gray;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content a.header-anchor:link,
|
|
||||||
.doc-content a.header-anchor:visited {
|
|
||||||
/*visibility: hidden;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content h1:hover a.header-anchor:hover,
|
|
||||||
.doc-content h2:hover a.header-anchor:hover,
|
|
||||||
.doc-content h3:hover a.header-anchor:hover,
|
|
||||||
.doc-content h4:hover a.header-anchor:hover,
|
|
||||||
.doc-content h5:hover a.header-anchor:hover,
|
|
||||||
.doc-content h6:hover a.header-anchor:hover {
|
|
||||||
/*color: #000;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content h1:hover a.header-anchor,
|
|
||||||
.doc-content h2:hover a.header-anchor,
|
|
||||||
.doc-content h3:hover a.header-anchor,
|
|
||||||
.doc-content h4:hover a.header-anchor,
|
|
||||||
.doc-content h5:hover a.header-anchor,
|
|
||||||
.doc-content h6:hover a.header-anchor {
|
|
||||||
/*color: #999;*/
|
|
||||||
/*visibility: visible;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content img {
|
|
||||||
width: 90%;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content img.orig-size {
|
|
||||||
width: auto;
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc-content .open-source-notice {
|
|
||||||
color: #666;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
text-align: center;
|
|
||||||
padding: 0.8em;
|
|
||||||
margin-top: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc {
|
|
||||||
padding: 1em;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-right {
|
|
||||||
float: right;
|
|
||||||
width: 40%;
|
|
||||||
margin: 0 0 0.5em 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc ul {
|
|
||||||
padding: 0 0 0 1.5em;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc a code {
|
|
||||||
color: #337ab7;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-left: 4px solid #e6522c;
|
|
||||||
border-radius: 0;
|
|
||||||
font-family: "Courier New", Monaco, Menlo, Consolas, monospace;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
color: #333;
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre code {
|
|
||||||
white-space: pre;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
aside {
|
|
||||||
color: #888;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
border-bottom: 1px solid #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
article {
|
|
||||||
margin: 10px 0 60px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*外部引用 样式end*/
|
|
||||||
</style>
|
|
||||||
<style lang="scss">
|
|
||||||
.explore-table tr td .cell {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.explore .promqlInput:not(:first-of-type) {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
1400
nezha-fronted/src/components/page/dashboard/explore/exploreItem.vue
Normal file
1400
nezha-fronted/src/components/page/dashboard/explore/exploreItem.vue
Normal file
File diff suppressed because it is too large
Load Diff
377
nezha-fronted/src/components/page/dashboard/explore/logTab.vue
Normal file
377
nezha-fronted/src/components/page/dashboard/explore/logTab.vue
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
<template>
|
||||||
|
<div class="log-detail">
|
||||||
|
<div id="logChart" class="log-chart">
|
||||||
|
<!-- <chart ref="logChart" :unit="unit" chart-type="logBar" :show-toolbox="false"></chart>-->
|
||||||
|
</div>
|
||||||
|
<div class="log-operations">
|
||||||
|
<div class="log-operation">
|
||||||
|
<span class="operation-label">{{$t('overall.time')}}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="time"
|
||||||
|
:active-color="theme.themeColor"
|
||||||
|
>
|
||||||
|
</el-switch>
|
||||||
|
</div>
|
||||||
|
<div class="log-operation">
|
||||||
|
<span class="operation-label">{{$t('dashboard.explore.descending')}}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="operations.descending"
|
||||||
|
:active-color="theme.themeColor"
|
||||||
|
>
|
||||||
|
</el-switch>
|
||||||
|
</div>
|
||||||
|
<div class="log-operation">
|
||||||
|
<span class="operation-label">{{$t('dashboard.explore.wrapLines')}}</span>
|
||||||
|
<el-switch
|
||||||
|
v-model="wrapLines"
|
||||||
|
:active-color="theme.themeColor"
|
||||||
|
>
|
||||||
|
</el-switch>
|
||||||
|
</div>
|
||||||
|
<div class="log-operation">
|
||||||
|
<span class="operation-label">{{$t('overall.limit')}}:</span>
|
||||||
|
<el-select v-model="operations.limit" size="small" style="width: 100px;">
|
||||||
|
<el-option v-for="option in limitOptions" :key="option" :value="option"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="log-operation">
|
||||||
|
<span class="operation-label">Result:</span>
|
||||||
|
<span>{{tableData.length}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="log-operation log-operation--right">
|
||||||
|
<button class="top-tool-btn" type="button" @click="exportLog"><i class="nz-icon nz-icon-download"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="log-table">
|
||||||
|
<el-table
|
||||||
|
:cell-class-name="wrapLines ? '': 'log-row-wrap--no-wrap'"
|
||||||
|
:data="tableData"
|
||||||
|
:show-header="false"
|
||||||
|
class="nz-table2"
|
||||||
|
size="mini"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
type="expand"
|
||||||
|
>
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<pre>{{row.labels}}</pre>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
v-if="time"
|
||||||
|
prop="timestamp"
|
||||||
|
width="140"
|
||||||
|
>
|
||||||
|
<template slot-scope="{ row }">{{row.timestamp}}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="message"
|
||||||
|
>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import axios from 'axios'
|
||||||
|
export default {
|
||||||
|
name: 'logTab',
|
||||||
|
props: {
|
||||||
|
unit: Number,
|
||||||
|
logData: Array
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
tableTimeFormat () {
|
||||||
|
return this.timeFormat
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
operations: {
|
||||||
|
levels: [0, 1, 2, 3, 4, 5, 6],
|
||||||
|
limit: 1000,
|
||||||
|
descending: true
|
||||||
|
},
|
||||||
|
time: true, // 换行和时间不需要处理数据
|
||||||
|
wrapLines: true,
|
||||||
|
|
||||||
|
limitOptions: [300, 1000, 3000, 10000],
|
||||||
|
levelOptions: [
|
||||||
|
{
|
||||||
|
type: 'trace',
|
||||||
|
keywords: ['trace'],
|
||||||
|
color: '#6ed0e0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'debug',
|
||||||
|
keywords: ['debug', 'dbug'],
|
||||||
|
color: '#1f78c1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'info',
|
||||||
|
keywords: ['info', 'information', 'informational', 'notice'],
|
||||||
|
color: '#7eb26d'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'warn',
|
||||||
|
keywords: ['warn', 'warning'],
|
||||||
|
color: '#ff851b'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'error',
|
||||||
|
keywords: ['error', 'err'],
|
||||||
|
color: '#e24d42'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'fatal',
|
||||||
|
keywords: ['emerg', 'critical', 'fatal', 'crit'],
|
||||||
|
color: '#705da0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'unknown',
|
||||||
|
keywords: [],
|
||||||
|
color: '#dde4ed'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tableData: [],
|
||||||
|
timeFormatData: [],
|
||||||
|
tableChartData: [],
|
||||||
|
myChart: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
exportLog () {
|
||||||
|
this.$emit('exportLog', this.operations)
|
||||||
|
},
|
||||||
|
timeFormat (timestamp) {
|
||||||
|
const timeLength = `${timestamp}`.length
|
||||||
|
// 判断时间是秒/毫秒/微秒/纳秒
|
||||||
|
let step = null
|
||||||
|
switch (timeLength) {
|
||||||
|
case 10:
|
||||||
|
step = 1000
|
||||||
|
break
|
||||||
|
case 13:
|
||||||
|
step = 1
|
||||||
|
break
|
||||||
|
case 16:
|
||||||
|
step = 0.001
|
||||||
|
break
|
||||||
|
case 19:
|
||||||
|
step = 0.000001
|
||||||
|
break
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
return this.utcTimeToTimezoneStr(timestamp * step)
|
||||||
|
},
|
||||||
|
loadChart () {
|
||||||
|
this.myChart = echarts.init(document.getElementById('logChart'))
|
||||||
|
if (this.tableChartData.length > 0) {
|
||||||
|
let series = this.tableChartData.map(d => ({
|
||||||
|
type: 'bar',
|
||||||
|
name: d.name,
|
||||||
|
stack: 'total',
|
||||||
|
barWidth: 6,
|
||||||
|
data: d.data.map(item => [item[0], item[1]]),
|
||||||
|
itemStyle: { color: this.levelOptions.find(l => l.type === d.name).color }
|
||||||
|
}))
|
||||||
|
series = series.sort((a, b) => {
|
||||||
|
return this.levelOptions.findIndex(l => a.name === l.type) - this.levelOptions.findIndex(l => b.name === l.type)
|
||||||
|
})
|
||||||
|
const option = {
|
||||||
|
title: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
top: 20,
|
||||||
|
left: 10,
|
||||||
|
right: 10,
|
||||||
|
bottom: 60,
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
bottom: 20
|
||||||
|
},
|
||||||
|
series,
|
||||||
|
xAxis: {
|
||||||
|
type: 'time',
|
||||||
|
axisTick: { show: false },
|
||||||
|
axisLabel: {
|
||||||
|
rotate: 0,
|
||||||
|
fontSize: 13 * window.devicePixelRatio,
|
||||||
|
formatter: '{HH}:{mm}:{ss}'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis'
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
axisLabel: {
|
||||||
|
fontSize: 13 * window.devicePixelRatio,
|
||||||
|
formatter (value, i) {
|
||||||
|
let y
|
||||||
|
if (value < 1000) {
|
||||||
|
y = value
|
||||||
|
} else if (value < 1000000) {
|
||||||
|
y = value / 1000 + ' K'
|
||||||
|
} else if (value >= 1000000) {
|
||||||
|
y = value / 1000000 + ' M'
|
||||||
|
} else {
|
||||||
|
y = value / 1000000000 + ' G'
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
useUTC: false // 使用本地时间
|
||||||
|
}
|
||||||
|
this.myChart.setOption(option)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
applyFilter (allTableData, filter) {
|
||||||
|
let data = [...allTableData]
|
||||||
|
// 过滤level
|
||||||
|
data = data.filter(d => {
|
||||||
|
const hit = filter.levels.some(l => {
|
||||||
|
return this.levelOptions[l].type === d.level.toLowerCase()
|
||||||
|
})
|
||||||
|
return hit
|
||||||
|
})
|
||||||
|
// limit
|
||||||
|
data = data.slice(0, filter.limit)
|
||||||
|
// 升降序
|
||||||
|
data = data.sort((a, b) => {
|
||||||
|
return filter.descending ? b.timestamp - a.timestamp : a.timestamp - b.timestamp
|
||||||
|
})
|
||||||
|
// logs内部上方的图表数据
|
||||||
|
const tableChartData = {}
|
||||||
|
data.forEach(d => {
|
||||||
|
tableChartData[d.level] || (tableChartData[d.level] = {})
|
||||||
|
tableChartData[d.level][`${d.timestamp}`] ? tableChartData[d.level][`${d.timestamp}`]++ : tableChartData[d.level][`${d.timestamp}`] = 1
|
||||||
|
})
|
||||||
|
const temp = []
|
||||||
|
for (const d in tableChartData) {
|
||||||
|
const level = { name: d, data: [] }
|
||||||
|
for (const time in tableChartData[d]) {
|
||||||
|
level.data.push([time, tableChartData[d][time]])
|
||||||
|
}
|
||||||
|
temp.push(level)
|
||||||
|
}
|
||||||
|
return { tableData: data, tableChartData: temp }
|
||||||
|
},
|
||||||
|
filterLogType (data) {
|
||||||
|
const logData = data.filter(l => l.resultType === 'streamsFormat')
|
||||||
|
let allTableData = []
|
||||||
|
// 合并
|
||||||
|
logData.forEach(d => {
|
||||||
|
allTableData = [...allTableData, ...d.result]
|
||||||
|
})
|
||||||
|
// 去重
|
||||||
|
const temp = []
|
||||||
|
allTableData = allTableData.reduce((cur, next) => {
|
||||||
|
if (temp.indexOf(next.uuid) === -1) {
|
||||||
|
temp.push(next.uuid)
|
||||||
|
cur.push(next)
|
||||||
|
}
|
||||||
|
return cur
|
||||||
|
}, [])
|
||||||
|
// 把时间调整为毫秒,并合并同毫秒的数据
|
||||||
|
allTableData = allTableData.map(d => ({
|
||||||
|
...d,
|
||||||
|
timestamp: this.timeFormat(d.timestamp)
|
||||||
|
}))
|
||||||
|
return { logData: allTableData }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
logData: {
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
handler (n, o) {
|
||||||
|
const { logData } = this.filterLogType(n) // 过滤出不同resultType,合并去重
|
||||||
|
const { tableData, tableChartData } = this.applyFilter(logData, this.operations) // 应用operation区域的过滤项
|
||||||
|
this.tableData = tableData
|
||||||
|
this.tableChartData = tableChartData
|
||||||
|
this.loadChart()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
operations: {
|
||||||
|
deep: true,
|
||||||
|
handler (n, o) {
|
||||||
|
const { logData } = this.filterLogType(this.logData)
|
||||||
|
const { tableData, tableChartData } = this.applyFilter(logData, this.operations) // 应用operation区域的过滤项
|
||||||
|
this.tableData = tableData
|
||||||
|
this.tableChartData = tableChartData
|
||||||
|
this.loadChart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.log-detail {
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.log-table .nz-table2 {
|
||||||
|
padding: 10px 0 0 0;
|
||||||
|
|
||||||
|
.el-table__body {
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0 6px;
|
||||||
|
|
||||||
|
td {
|
||||||
|
vertical-align: top;
|
||||||
|
padding: 1px 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
td.el-table__expanded-cell {
|
||||||
|
padding: 0 0 0 60px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
// 左侧边框
|
||||||
|
td:first-child {
|
||||||
|
border-left: 3px solid $--right-box-border-color;
|
||||||
|
}
|
||||||
|
td.el-table__expanded-cell:first-child {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-chart {
|
||||||
|
height: 300px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.log-operations {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 50px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 10px 0 20px;
|
||||||
|
border: 1px solid #E4E8EB;
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
.log-operation {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.log-operation--right {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
&:not(:last-of-type) {
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation-label {
|
||||||
|
padding-right: 10px;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -6,11 +6,11 @@
|
|||||||
<div v-if="plugins.indexOf('metric-selector') > -1">
|
<div v-if="plugins.indexOf('metric-selector') > -1">
|
||||||
<el-dropdown class="metric-selector">
|
<el-dropdown class="metric-selector">
|
||||||
<el-dropdown-menu style="display: none"></el-dropdown-menu>
|
<el-dropdown-menu style="display: none"></el-dropdown-menu>
|
||||||
<button class="top-tool-btn top-tool-btn--text" type="button" @click="toggleDropdown">{{ $t("overall.metric") }}
|
<button class="top-tool-btn top-tool-btn--text" type="button" @click="toggleDropdown">{{type === 'log' ? $t("overall.logLabels") : $t("overall.metric") }}
|
||||||
<i class="nz-icon nz-icon-arrow-down" style="font-size: 12px"></i></button>
|
<i class="nz-icon nz-icon-arrow-down" style="font-size: 12px"></i></button>
|
||||||
<el-cascader-panel v-show="dropDownVisible" ref="metricSelector" slot="dropdown" v-model="cascaderValue"
|
<el-cascader-panel v-show="dropDownVisible" ref="metricSelector" slot="dropdown" v-model="cascaderValue"
|
||||||
v-clickoutside="closeDropdown" v-loading="tempBoxShowLoading" :loading="loading" :options="metricOptions"
|
v-clickoutside="closeDropdown" v-loading="tempBoxShowLoading" :loading="loading" :options="metricOptions"
|
||||||
:props="{emitPath:false,}" @change="metricChangeNew">
|
v-if="type !== 'log'" :props="cascaderProps" @change="metricChangeNew">
|
||||||
|
|
||||||
<template slot-scope="{ node, data }">
|
<template slot-scope="{ node, data }">
|
||||||
<div :class="['nz-cascade',data.temp&&!data.child?'nz-cascade-temp':'']" @click="()=>{lazyLoad(node,data)}" :title="data.label">
|
<div :class="['nz-cascade',data.temp&&!data.child?'nz-cascade-temp':'']" @click="()=>{lazyLoad(node,data)}" :title="data.label">
|
||||||
@@ -18,7 +18,15 @@
|
|||||||
{{data.label}}
|
{{data.label}}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
</el-cascader-panel>
|
||||||
|
<el-cascader-panel v-else v-show="dropDownVisible" ref="metricSelector" slot="dropdown"
|
||||||
|
v-model="cascaderValue" v-clickoutside="closeDropdown" v-loading="tempBoxShowLoading"
|
||||||
|
:loading="loading" :props="cascaderProps" @change="logLabelChange">
|
||||||
|
<template slot-scope="{ node, data }">
|
||||||
|
<div :title="data.label" class="nz-cascade">
|
||||||
|
{{data.label}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</el-cascader-panel>
|
</el-cascader-panel>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
@@ -146,6 +154,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import selectAlertSilence from '../../../common/alert/selectAlertSilence'
|
import selectAlertSilence from '../../../common/alert/selectAlertSilence'
|
||||||
import editor from './editor'
|
import editor from './editor'
|
||||||
|
import { get } from '@/http'
|
||||||
export default {
|
export default {
|
||||||
name: 'promqlInput',
|
name: 'promqlInput',
|
||||||
components: {
|
components: {
|
||||||
@@ -176,6 +185,9 @@ export default {
|
|||||||
fromFatherData: {
|
fromFatherData: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String // metric和log两种,为空时视为metric
|
||||||
}
|
}
|
||||||
// metricOptions: {type: Array},
|
// metricOptions: {type: Array},
|
||||||
// metricStore: {type: Array}
|
// metricStore: {type: Array}
|
||||||
@@ -206,6 +218,47 @@ export default {
|
|||||||
loading: false
|
loading: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
cascaderProps () {
|
||||||
|
if (this.type === 'log') {
|
||||||
|
return {
|
||||||
|
lazy: true,
|
||||||
|
lazyLoad (node, resolve) {
|
||||||
|
const { level } = node
|
||||||
|
if (level === 0) {
|
||||||
|
get('/logs/loki/api/v1/labels').then(res => {
|
||||||
|
if (res.data) {
|
||||||
|
const nodes = res.data.sort().map(d => ({
|
||||||
|
value: d,
|
||||||
|
label: d,
|
||||||
|
leaf: false
|
||||||
|
}))
|
||||||
|
resolve(nodes)
|
||||||
|
} else {
|
||||||
|
resolve([])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (level === 1) {
|
||||||
|
get(`/logs/loki/api/v1/label/${node.value}/values`).then(res => {
|
||||||
|
if (res.data) {
|
||||||
|
const nodes = res.data.sort().map(d => ({
|
||||||
|
value: d,
|
||||||
|
label: d,
|
||||||
|
leaf: true
|
||||||
|
}))
|
||||||
|
resolve(nodes)
|
||||||
|
} else {
|
||||||
|
resolve([])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return { emitPath: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
if (!this.fromFatherData) {
|
if (!this.fromFatherData) {
|
||||||
this.queryMetrics()
|
this.queryMetrics()
|
||||||
@@ -304,6 +357,14 @@ export default {
|
|||||||
this.$forceUpdate()
|
this.$forceUpdate()
|
||||||
this.cascaderValue = ''
|
this.cascaderValue = ''
|
||||||
},
|
},
|
||||||
|
logLabelChange (value) {
|
||||||
|
if (!value || value.length === 0) return
|
||||||
|
this.expressionList[this.index] = `{${value[0]}="${value[1]}"}`
|
||||||
|
this.dropDownVisible = false
|
||||||
|
this.$emit('change', value)
|
||||||
|
this.$forceUpdate()
|
||||||
|
this.cascaderValue = ''
|
||||||
|
},
|
||||||
metricKeyDown (val) {
|
metricKeyDown (val) {
|
||||||
if (this.required) {
|
if (this.required) {
|
||||||
this.metricChange(val)
|
this.metricChange(val)
|
||||||
@@ -313,7 +374,6 @@ export default {
|
|||||||
this.$emit('change')
|
this.$emit('change')
|
||||||
},
|
},
|
||||||
setError: function (errMsg) {
|
setError: function (errMsg) {
|
||||||
// console.log(errMsg)
|
|
||||||
this.errorMsg = errMsg
|
this.errorMsg = errMsg
|
||||||
},
|
},
|
||||||
getExprTemp () {
|
getExprTemp () {
|
||||||
@@ -528,7 +588,7 @@ export default {
|
|||||||
} */
|
} */
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
dropDownVisible: function (n, o) {
|
dropDownVisible (n, o) {
|
||||||
if (this.$refs.metricSelector) {
|
if (this.$refs.metricSelector) {
|
||||||
this.$refs.metricSelector.dropDownVisible = n
|
this.$refs.metricSelector.dropDownVisible = n
|
||||||
if (!this.expressionList[this.index] || this.expressionList[this.index] == '') {
|
if (!this.expressionList[this.index] || this.expressionList[this.index] == '') {
|
||||||
|
|||||||
@@ -413,6 +413,7 @@ export default {
|
|||||||
defaultTooltipFormatter: function (params) {
|
defaultTooltipFormatter: function (params) {
|
||||||
let minusFlag = true
|
let minusFlag = true
|
||||||
let str = '<div>'
|
let str = '<div>'
|
||||||
|
params instanceof Array || (params = [params])
|
||||||
params.forEach((item, i) => {
|
params.forEach((item, i) => {
|
||||||
const alias = this.queryAlias(i)
|
const alias = this.queryAlias(i)
|
||||||
if (i === 0 && alias.indexOf('Previous ') === -1) {
|
if (i === 0 && alias.indexOf('Previous ') === -1) {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const commonOption = {
|
|||||||
tooltip: { // 和 option.tooltip 的配置项相同
|
tooltip: { // 和 option.tooltip 的配置项相同
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
formatter: function (param) {
|
formatter (param) {
|
||||||
return param.title // 自定义的 DOM 结构
|
return param.title // 自定义的 DOM 结构
|
||||||
},
|
},
|
||||||
backgroundColor: 'rgba(255,255,255,0)',
|
backgroundColor: 'rgba(255,255,255,0)',
|
||||||
@@ -103,7 +103,7 @@ const commonOption = {
|
|||||||
axisLabel: {
|
axisLabel: {
|
||||||
interval: 0,
|
interval: 0,
|
||||||
rotate: 0,
|
rotate: 0,
|
||||||
formatter: function (value, index) {
|
formatter (value, index) {
|
||||||
const tData = new Date(value)
|
const tData = new Date(value)
|
||||||
return [tData.getFullYear(), tData.getMonth() + 1, tData.getDate()].join('-') + '\n' +
|
return [tData.getFullYear(), tData.getMonth() + 1, tData.getDate()].join('-') + '\n' +
|
||||||
[tData.getHours(), tData.getMinutes()].join(':')
|
[tData.getHours(), tData.getMinutes()].join(':')
|
||||||
@@ -251,7 +251,7 @@ const alertMessageBarByRule = {
|
|||||||
axisLabel: {
|
axisLabel: {
|
||||||
show: true,
|
show: true,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
formatter: function (value) {
|
formatter (value) {
|
||||||
if (value.length > 15) {
|
if (value.length > 15) {
|
||||||
return '...' + value.substring(value.length - 12, value.length)
|
return '...' + value.substring(value.length - 12, value.length)
|
||||||
}
|
}
|
||||||
@@ -305,7 +305,7 @@ const alertMessageBarByAsset = {
|
|||||||
axisLabel: {
|
axisLabel: {
|
||||||
show: true,
|
show: true,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
formatter: function (value) {
|
formatter (value) {
|
||||||
let r = value
|
let r = value
|
||||||
if (r.length > 4) {
|
if (r.length > 4) {
|
||||||
r = value.substring(0, 3) + '...'
|
r = value.substring(0, 3) + '...'
|
||||||
@@ -316,7 +316,62 @@ const alertMessageBarByAsset = {
|
|||||||
triggerEvent: true
|
triggerEvent: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const logBar = {
|
||||||
|
title: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'time',
|
||||||
|
axisLabel: {
|
||||||
|
rotate: 0,
|
||||||
|
fontSize: 13 * window.devicePixelRatio
|
||||||
|
},
|
||||||
|
axisPointer: { // y轴上显示指针对应的值
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
splitLine: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
fontSize: 13 * window.devicePixelRatio,
|
||||||
|
formatter (value, i) {
|
||||||
|
let y
|
||||||
|
if (value < 1000) {
|
||||||
|
} else if (value < 1000000) {
|
||||||
|
y = value / 1000 + ' K'
|
||||||
|
} else if (value >= 1000000) {
|
||||||
|
y = value / 1000000 + ' M'
|
||||||
|
} else {
|
||||||
|
y = value / 1000000000 + ' G'
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
useUTC: false, // 使用本地时间
|
||||||
|
series: []
|
||||||
|
}
|
||||||
const overviewLine = {
|
const overviewLine = {
|
||||||
title: {
|
title: {
|
||||||
show: false
|
show: false
|
||||||
@@ -332,7 +387,7 @@ const overviewLine = {
|
|||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
confine: false,
|
confine: false,
|
||||||
extraCssText: 'z-index:1000;',
|
extraCssText: 'z-index:1000;',
|
||||||
formatter: function (v, i) {
|
formatter (v, i) {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -375,7 +430,7 @@ const overviewLine = {
|
|||||||
},
|
},
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
fontSize: 13 * window.devicePixelRatio,
|
fontSize: 13 * window.devicePixelRatio,
|
||||||
formatter: function (value, i) {
|
formatter (value, i) {
|
||||||
let y
|
let y
|
||||||
if (value < 1000) {
|
if (value < 1000) {
|
||||||
y = value + ' Bs'
|
y = value + ' Bs'
|
||||||
@@ -524,22 +579,23 @@ const chartTypes = {
|
|||||||
bar: { name: 'alertMessage', option: alertMessageBarByAsset },
|
bar: { name: 'alertMessage', option: alertMessageBarByAsset },
|
||||||
ruleBar: { name: 'ruleMessage', option: alertMessageBarByRule },
|
ruleBar: { name: 'ruleMessage', option: alertMessageBarByRule },
|
||||||
assetBar: { name: 'assetMessage', option: alertMessageBarByAsset },
|
assetBar: { name: 'assetMessage', option: alertMessageBarByAsset },
|
||||||
|
logBar: { name: 'logBar', option: logBar },
|
||||||
noData: { name: 'noData', option: noDataOption },
|
noData: { name: 'noData', option: noDataOption },
|
||||||
tooltipPie: { option: tooltipPieOption },
|
tooltipPie: { option: tooltipPieOption },
|
||||||
topoPie: { option: topoPieOption }
|
topoPie: { option: topoPieOption }
|
||||||
}
|
}
|
||||||
export default {
|
export default {
|
||||||
getOption: function (type) {
|
getOption (type) {
|
||||||
return JSON.parse(JSON.stringify(chartTypes[type].option))
|
return JSON.parse(JSON.stringify(chartTypes[type].option))
|
||||||
},
|
},
|
||||||
getOptionNoData: function (type) {
|
getOptionNoData (type) {
|
||||||
chartTypes[type].option.xAxis.data = createTempTimes()
|
chartTypes[type].option.xAxis.data = createTempTimes()
|
||||||
return JSON.parse(JSON.stringify(chartTypes[type].option))
|
return JSON.parse(JSON.stringify(chartTypes[type].option))
|
||||||
},
|
},
|
||||||
setMap: function (map) {
|
setMap (map) {
|
||||||
mapOptions.geo.map = map
|
mapOptions.geo.map = map
|
||||||
},
|
},
|
||||||
getBgColorList: function () {
|
getBgColorList () {
|
||||||
return Object.assign([], bgColorList)
|
return Object.assign([], bgColorList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<pick-time id="panel" ref="pickTime" v-model="searchTime" :refresh-data-func="dateChange" :use-chart-unit="false"></pick-time>
|
<pick-time id="panel" ref="pickTime" v-model="searchTime" :refresh-data-func="dateChange" :use-chart-unit="false" class="margin-r-10"></pick-time>
|
||||||
|
|
||||||
<button id="panel-add-chart" v-has="'panel_chart_add'" :title="$t('overall.createChart')" class="top-tool-btn margin-r-10"
|
<button id="panel-add-chart" v-has="'panel_chart_add'" :title="$t('overall.createChart')" class="top-tool-btn margin-r-10"
|
||||||
type="button" @click="addChart">
|
type="button" @click="addChart">
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default new Router({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/explore',
|
path: '/explore',
|
||||||
component: resolve => require(['../components/page/dashboard/explore/explore.vue'], resolve)
|
component: resolve => require(['@/components/page/dashboard/explore/explore'], resolve)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/overview',
|
path: '/overview',
|
||||||
|
|||||||
Reference in New Issue
Block a user