You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by mi...@apache.org on 2019/03/21 02:06:08 UTC
[incubator-dubbo-admin] branch develop updated: add echarts to
implement metrics
This is an automated email from the ASF dual-hosted git repository.
min pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo-admin.git
The following commit(s) were added to refs/heads/develop by this push:
new 1faaec0 add echarts to implement metrics
1faaec0 is described below
commit 1faaec0b747604897c4a9af6c67222744231ecd7
Author: nzomkxia <z8...@gmail.com>
AuthorDate: Thu Mar 21 10:05:58 2019 +0800
add echarts to implement metrics
---
dubbo-admin-ui/index.html | 1 +
dubbo-admin-ui/src/api/chart.js | 96 +++++++++
.../src/components/metrics/ServiceMetrics.vue | 228 +++++++++++++++++++--
dubbo-admin-ui/src/components/public/MiniChart.vue | 86 ++++++++
dubbo-admin-ui/src/lang/en.js | 5 +
dubbo-admin-ui/src/lang/zh.js | 5 +
dubbo-admin-ui/src/util/echart.js | 215 +++++++++++++++++++
7 files changed, 622 insertions(+), 14 deletions(-)
diff --git a/dubbo-admin-ui/index.html b/dubbo-admin-ui/index.html
index 2718725..a4f69a5 100644
--- a/dubbo-admin-ui/index.html
+++ b/dubbo-admin-ui/index.html
@@ -23,6 +23,7 @@
<title>Dubbo Admin</title>
<link href='/static/OpenSans.css' rel="stylesheet" type="text/css">
<link rel="shortcut icon" href="/static/dubbo.ico" type="image/x-icon">
+ <script src="https://cdn.bootcss.com/echarts/4.0.4/echarts-en.min.js"></script>
</head>
<body>
<div id="app"></div>
diff --git a/dubbo-admin-ui/src/api/chart.js b/dubbo-admin-ui/src/api/chart.js
new file mode 100644
index 0000000..4190c7b
--- /dev/null
+++ b/dubbo-admin-ui/src/api/chart.js
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const range = (start, end) => new Array(end - start).fill(start).map((el, i) => start + i)
+
+const shortMonth = [
+ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
+]
+const monthVisitData = shortMonth.map(m => {
+ return {
+ 'month': m,
+ 'Unique Visit': Math.floor(Math.random() * 1000) + 200,
+ 'Page View': Math.floor(Math.random() * 1000) + 250
+ }
+})
+
+const campaignData = [
+ {
+ value: 335,
+ name: 'Website'
+ },
+ {
+ value: 310,
+ name: 'Email'
+ },
+ {
+ value: 234,
+ name: 'Ads'
+ },
+ {
+ value: 135,
+ name: 'Video'
+ },
+ {
+ value: 1548,
+ name: 'Search'
+ }
+]
+const locationData = [
+ {
+ value: 50,
+ name: 'China'
+ },
+ {
+ value: 35,
+ name: 'USA'
+ },
+ {
+ value: 25,
+ name: 'EU'
+ },
+ {
+ value: 10,
+ name: 'Russia'
+ },
+ {
+ value: 10,
+ name: 'Other'
+ }
+]
+
+const StackMainData = [220, 182, 191, 234, 290, 330, 310, 123, 442, 321, 90, 149, 210, 122, 133, 334, 198, 123, 125, 220]
+const StackData = StackMainData.map((item, key) => {
+ return {
+ 'label': key + 'D',
+ 'max': 500,
+ 'sales': item
+ }
+})
+const SinData = range(1, 12).map(i => {
+ return {
+ 'cate': 'Cat' + i,
+ 'value': ((Math.sin(i / 5) * (i / 5 - 0.1) + i / 6) * 5)
+ }
+})
+
+export {
+ monthVisitData,
+ campaignData,
+ locationData,
+ StackData,
+ SinData
+}
diff --git a/dubbo-admin-ui/src/components/metrics/ServiceMetrics.vue b/dubbo-admin-ui/src/components/metrics/ServiceMetrics.vue
index 204083a..e1765a9 100644
--- a/dubbo-admin-ui/src/components/metrics/ServiceMetrics.vue
+++ b/dubbo-admin-ui/src/components/metrics/ServiceMetrics.vue
@@ -16,27 +16,227 @@
-->
<template>
- <v-container
- fill-height
- >
- <v-layout align-center>
- <v-flex text-xs-center>
- <h1 class="display-2 primary--text">{{$t('later.metrics')}}</h1>
- <v-btn
- href="#/service"
- color="primary"
- outline
- >
- {{$t('goIndex')}}
- </v-btn>
+ <v-container grid-list-xl fluid>
+ <v-layout row wrap>
+ <v-flex lg12>
+ <breadcrumb title="metrics" :items="breads"></breadcrumb>
+ </v-flex>
+ <v-flex xs12 >
+ <search id="serviceSearch" v-model="filter" :submit="submit" :label="$t('searchSingleMetrics')"></search>
+ </v-flex>
+ <v-flex lg4 sm6 xs12>
+ <v-card>
+ <v-card-text>
+ <mini-chart
+ title="Monthly Sales"
+ sub-title="10%"
+ icon="trending_up"
+ :data="dataset.monthVisit"
+ :chart-color="color.blue.base"
+ type="line"
+ >
+ </mini-chart>
+ <mini-chart
+ title="Monthly Sales"
+ sub-title="10%"
+ icon="trending_up"
+ :data="dataset.monthVisit"
+ :chart-color="color.blue.base"
+ type="line"
+ >
+ </mini-chart>
+ </v-card-text>
+ </v-card>
+ </v-flex>
+ <v-flex lg4 sm6 xs12>
+ <v-card>
+ <v-card-text>
+ <mini-chart
+ title="Monthly Sales"
+ sub-title="10%"
+ icon="trending_up"
+ :data="dataset.monthVisit"
+ :chart-color="color.blue.base"
+ type="line"
+ >
+ </mini-chart>
+ <mini-chart
+ title="Monthly Sales"
+ sub-title="10%"
+ icon="trending_up"
+ :data="dataset.monthVisit"
+ :chart-color="color.blue.base"
+ type="line"
+ >
+ </mini-chart>
+ </v-card-text>
+ </v-card>
+ </v-flex>
+ <v-flex lg4 sm6 xs12>
+ <v-card>
+ <v-card-text>
+ <mini-chart
+ title="Monthly Sales"
+ sub-title="10%"
+ icon="trending_up"
+ :data="dataset.monthVisit"
+ :chart-color="color.blue.base"
+ type="line"
+ >
+ </mini-chart>
+ <mini-chart
+ title="Monthly Sales"
+ sub-title="10%"
+ icon="trending_up"
+ :data="dataset.monthVisit"
+ :chart-color="color.blue.base"
+ type="line"
+ >
+ </mini-chart>
+ </v-card-text>
+ </v-card>
+ </v-flex>
+ <v-flex sm12>
+ <h3>{{$t('methodMetrics')}}</h3>
+ </v-flex>
+ <v-flex lg12 >
+ <v-tabs
+ class="elevation-1">
+ <v-tab>
+ {{$t('providers')}}
+ </v-tab>
+ <v-tab>
+ {{$t('consumers')}}
+ </v-tab>
+ <v-tab-item>
+ <v-data-table
+ class="elevation-1"
+ :headers="headers"
+ :items="providerDetails"
+ >
+ <template slot="items" slot-scope="props">
+ <td>{{props.item.service}}</td>
+ <td>{{props.item.method}}</td>
+ <td>{{props.item.qps}}</td>
+ <td>{{props.item.rt}}</td>
+ <td>{{props.item.successRate}}</td>
+ </template>
+ </v-data-table>
+ </v-tab-item>
+ <v-tab-item >
+ <v-data-table
+ class="elevation-1"
+ :headers="headers"
+ :items="consumerDetails"
+ >
+ <template slot="items" slot-scope="props">
+ <td>{{props.item.service}}</td>
+ <td>{{props.item.method}}</td>
+ <td>{{props.item.qps}}</td>
+ <td>{{props.item.rt}}</td>
+ <td>{{props.item.successRate}}</td>
+ </template>
+ </v-data-table>
+ </v-tab-item>
+ </v-tabs>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
+ import EChart from '@/util/echart'
+ import Material from 'vuetify/es5/util/colors'
+ import MiniChart from '@/components/public/MiniChart'
+ import Breadcrumb from '@/components/public/Breadcrumb'
+ import Search from '@/components/public/Search'
+ import {
+ monthVisitData,
+ campaignData,
+ locationData,
+ StackData,
+ SinData
+ } from '@/api/chart'
export default {
- name: 'ServiceMetrics'
+ name: 'ServiceMetrics',
+ components: {
+ MiniChart,
+ EChart,
+ Breadcrumb,
+ Search
+ },
+ data () {
+ return {
+ selectedTab: 'tab-1',
+ filter: '',
+ headers: [],
+ providerDetails: [
+ {
+ service: 'a.b.c.d',
+ method: 'aaaa~ICS',
+ qps: '0.58',
+ rt: '111',
+ successRate: '100%'
+ },
+ {
+ service: 'a.b.c.f',
+ method: 'bbbb~ICS',
+ qps: '0.87',
+ rt: '120',
+ successRate: '90%'
+ }
+
+ ],
+ consumerDetails: [],
+ option: null,
+ dataset: {
+ sinData: SinData,
+ monthVisit: monthVisitData,
+ campaign: campaignData,
+ location: locationData,
+ stackData: StackData
+ },
+ color: Material,
+ breads: [
+ {
+ text: 'metrics',
+ href: ''
+ }
+ ]
+
+ }
+ },
+ methods: {
+ submit: function () {
+ },
+ setHeaders: function () {
+ this.headers = [
+ {
+ text: this.$t('service'),
+ value: 'service'
+ },
+ {
+ text: this.$t('method'),
+ value: 'method'
+ },
+ {
+ text: this.$t('qps'),
+ value: 'qps'
+ },
+ {
+ text: this.$t('rt'),
+ value: 'rt'
+ },
+ {
+ text: this.$t('successRate'),
+ value: 'successRate'
+ }
+ ]
+ }
+ },
+ mounted: function () {
+ this.setHeaders()
+ }
}
</script>
diff --git a/dubbo-admin-ui/src/components/public/MiniChart.vue b/dubbo-admin-ui/src/components/public/MiniChart.vue
new file mode 100644
index 0000000..bc087e9
--- /dev/null
+++ b/dubbo-admin-ui/src/components/public/MiniChart.vue
@@ -0,0 +1,86 @@
+<!--
+ - Licensed to the Apache Software Foundation (ASF) under one or more
+ - contributor license agreements. See the NOTICE file distributed with
+ - this work for additional information regarding copyright ownership.
+ - The ASF licenses this file to You under the Apache License, Version 2.0
+ - (the "License"); you may not use this file except in compliance with
+ - the License. You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -->
+
+<template>
+ <div class="layout row ma-0 align-center justify-space-between">
+ <div class="text-box">
+ <div class="subheading pb-2">{{title}}</div>
+ <span class="grey--text">{{subTitle}} <v-icon small :color="iconColor">{{icon}}</v-icon></span>
+ </div>
+ <div class="chart">
+ <e-chart
+ :path-option="computeChartOption"
+ height="68px"
+ width="100%"
+ >
+ </e-chart>
+ </div>
+ </div>
+</template>
+
+<script>
+ import EChart from '@/util/echart'
+ export default {
+ components: {
+ EChart
+ },
+ props: {
+ title: String,
+ subTitle: String,
+ icon: String,
+ iconColor: {
+ type: String,
+ default: 'success'
+ },
+ type: String,
+ chartColor: String,
+ data: Array
+ },
+ data () {
+ return {
+ defaultOption: [
+ ['dataset.source', this.data],
+ ['xAxis.show', false],
+ ['yAxis.show', false],
+ ['color', [this.chartColor]]
+ ]
+ }
+ },
+
+ computed: {
+ computeChartOption () {
+ switch (this.type) {
+ case 'bar':
+ this.defaultOption.push(['series[0].type', 'bar'])
+ break
+ case 'area':
+ this.defaultOption.push(['series[0].type', 'line'])
+ this.defaultOption.push(['series[0].areaStyle', {}])
+ break
+ default:
+ break
+ }
+ return this.defaultOption
+ }
+ }
+
+ }
+</script>
+
+<style scoped>
+
+</style>
diff --git a/dubbo-admin-ui/src/lang/en.js b/dubbo-admin-ui/src/lang/en.js
index af39aff..405fa04 100644
--- a/dubbo-admin-ui/src/lang/en.js
+++ b/dubbo-admin-ui/src/lang/en.js
@@ -34,6 +34,9 @@ export default {
version: 'Version',
app: 'Application',
ip: 'IP',
+ qps: 'qps',
+ rt: 'rt',
+ successRate: 'success rate',
port: 'PORT',
timeout: 'timeout(ms)',
serialization: 'serialization',
@@ -72,11 +75,13 @@ export default {
appNameHint: 'Application name the service belongs to',
basicInfo: 'BasicInfo',
metaData: 'MetaData',
+ methodMetrics: 'Method Statistics',
searchDubboService: 'Search Dubbo Services or applications',
serviceSearchHint: 'Service ID, org.apache.dubbo.demo.api.DemoService, * for all services',
ipSearchHint: 'Find all services provided by the target server on the specified IP address',
appSearchHint: 'Input an application name to find all services provided by one particular application, * for all',
searchTagRule: 'Search Tag Rule by application name',
+ searchSingleMetrics: 'Search Metrics by IP',
searchBalanceRule: 'Search Balancing Rule',
noMetadataHint: 'There is no metadata available, please update to Dubbo2.7, or check your config center configuration in application.properties, please check ',
parameterList: 'parameterList',
diff --git a/dubbo-admin-ui/src/lang/zh.js b/dubbo-admin-ui/src/lang/zh.js
index 9a03cfe..3f863a1 100644
--- a/dubbo-admin-ui/src/lang/zh.js
+++ b/dubbo-admin-ui/src/lang/zh.js
@@ -33,6 +33,9 @@ export default {
version: '版本',
app: '应用',
ip: 'IP地址',
+ qps: 'qps',
+ rt: 'rt',
+ successRate: '成功率',
serviceInfo: '服务信息',
port: '端口',
timeout: '超时(毫秒)',
@@ -72,11 +75,13 @@ export default {
appNameHint: '服务所属的应用名称',
basicInfo: '基础信息',
metaData: '元数据',
+ methodMetrics: '服务方法统计',
searchDubboService: '搜索Dubbo服务或应用',
serviceSearchHint: '服务ID, org.apache.dubbo.demo.api.DemoService, * 代表所有服务',
ipSearchHint: '在指定的IP地址上查找目标服务器提供的所有服务',
appSearchHint: '输入应用名称以查找由一个特定应用提供的所有服务, * 代表所有',
searchTagRule: '根据应用名搜索标签规则',
+ searchSingleMetrics: '输入IP搜索Metrics信息',
searchBalanceRule: '搜索负载均衡规则',
parameterList: '参数列表',
returnType: '返回值',
diff --git a/dubbo-admin-ui/src/util/echart.js b/dubbo-admin-ui/src/util/echart.js
new file mode 100644
index 0000000..3660f21
--- /dev/null
+++ b/dubbo-admin-ui/src/util/echart.js
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ECharts Vue Wrapper
+ * Michael Wang
+ */
+import colors from 'vuetify/es5/util/colors'
+import _object from 'lodash/object'
+
+const ECharts = window.echarts || undefined
+if (ECharts === undefined) {
+ console.error('ECharts is not defined')
+}
+// set color palette
+const colorPalette = []
+
+Object.entries(colors).forEach((item) => {
+ if (item[1].base) {
+ colorPalette.push(item[1].base)
+ }
+});
+
+(function () {
+ const throttle = function (type, name, obj) {
+ obj = obj || window
+ let running = false
+ let func = function () {
+ if (running) { return }
+ running = true
+ requestAnimationFrame(function () {
+ obj.dispatchEvent(new CustomEvent(name))
+ running = false
+ })
+ }
+ obj.addEventListener(type, func)
+ }
+ /* init - you can init any event */
+ throttle('resize', 'optimizedResize')
+})()
+export default {
+ name: 'v-echart',
+
+ render (h) {
+ const data = {
+ staticClass: 'v-chart',
+ style: this.canvasStyle,
+ ref: 'canvas',
+ on: this.$listeners
+ }
+ return h('div', data)
+ },
+
+ props: {
+ // args of ECharts.init(dom, theme, opts)
+ width: { type: String, default: 'auto' },
+ height: { type: String, default: '400px' },
+ merged: {
+ type: Boolean,
+ default: true
+ },
+ // instace.setOption
+ pathOption: [Object, Array],
+ option: Object,
+ // general config
+ textStyle: Object,
+ title: Object,
+ legend: [Object, Array],
+ tooltip: Object,
+ grid: { type: [Object, Array] },
+ xAxis: [Object, Array],
+ yAxis: [Object, Array],
+ series: [Object, Array],
+ axisPointer: Object,
+ dataset: { type: [Object, Array], default () { return {} } }, // option.dataSet
+ colors: Array, // echarts.option.color
+ backgroundColor: [Object, String],
+ toolbox: { type: [Object, Array] },
+ // resize delay
+ widthChangeDelay: {
+ type: Number,
+ default: 450
+ }
+ },
+ data: () => ({
+ chartInstance: null,
+ clientWidth: null,
+ allowedOptions: [
+ 'textStyle', 'title', 'legend', 'xAxis',
+ 'yAxis', 'series', 'tooltip', 'axisPointer',
+ 'grid', 'dataset', 'colors', 'backgroundColor'
+ ],
+ _defaultOption: {
+ tooltip: {
+ show: true
+ },
+ title: {
+ show: true,
+ textStyle: {
+ color: 'rgba(0, 0, 0 , .87)',
+ fontFamily: 'sans-serif'
+ }
+ },
+ grid: {
+ containLabel: true
+ },
+ xAxis: {
+ show: true,
+ type: 'category',
+ axisLine: {
+ lineStyle: {
+ color: 'rgba(0, 0, 0 , .54)',
+ type: 'dashed'
+ }
+ },
+ axisTick: {
+ show: true,
+ alignWithLabel: true,
+ lineStyle: {
+ show: true,
+ color: 'rgba(0, 0, 0 , .54)',
+ type: 'dashed'
+ }
+ },
+ axisLabel: {
+ show: false
+ }
+ },
+ yAxis: {
+ show: true,
+ type: 'value',
+ axisLine: {
+ lineStyle: {
+ color: 'rgba(0, 0, 0 , .54)',
+ type: 'dashed'
+ }
+ },
+ axisLabel: {
+ show: false
+ },
+ splitLine: {
+ lineStyle: {
+ type: 'dashed'
+ }
+ },
+ axisTick: {
+ show: true,
+ lineStyle: {
+ show: true,
+ color: 'rgba(0, 0, 0 , .54)',
+ type: 'dashed'
+ }
+ }
+ },
+ series: [{
+ type: 'line'
+ }]
+
+ }
+ }),
+ computed: {
+ canvasStyle () {
+ return {
+ width: this.width,
+ height: this.height
+ }
+ }
+ },
+ methods: {
+ init () {
+ // set
+ if (this.pathOption) {
+ this.pathOption.forEach((p) => {
+ _object.set(this.$data._defaultOption, p[0], p[1])
+ })
+ }
+ this.chartInstance = ECharts.init(this.$refs.canvas, 'material')
+ this.chartInstance.setOption(_object.merge(this.option, this.$data._defaultOption))
+ window.addEventListener('optimizedResize', (e) => {
+ setTimeout(_ => {
+ this.chartInstance.resize()
+ }, this.widthChangeDelay)
+ })
+ },
+
+ resize () {
+ this.chartInstance.resize()
+ },
+ clean () {
+ window.removeEventListener('resize', this.chartInstance.resize)
+ this.chartInstance.clear()
+ }
+ },
+ mounted () {
+ this.init()
+ },
+
+ beforeDestroy () {
+ this.clean()
+ }
+}