[web-app] 监控历史图表详情

This commit is contained in:
tomsun28
2022-01-30 14:00:46 +08:00
parent ef7f71952b
commit c661dfcdbb
15 changed files with 493 additions and 96 deletions

View File

@@ -1,8 +1,9 @@
package com.usthe.manager.controller; package com.usthe.manager.controller;
import com.usthe.common.entity.dto.Message; import com.usthe.common.entity.dto.Message;
import com.usthe.manager.pojo.dto.Hierarchy; import com.usthe.common.entity.job.Job;
import com.usthe.common.entity.manager.ParamDefine; import com.usthe.common.entity.manager.ParamDefine;
import com.usthe.manager.pojo.dto.Hierarchy;
import com.usthe.manager.service.AppService; import com.usthe.manager.service.AppService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@@ -40,6 +41,14 @@ public class AppController {
return ResponseEntity.ok(new Message<>(paramDefines)); return ResponseEntity.ok(new Message<>(paramDefines));
} }
@GetMapping(path = "/{app}/define")
@ApiOperation(value = "查询监控类型的结构定义", notes = "根据app查询指定监控类型的定义结构")
public ResponseEntity<Message<Job>> queryAppDefine(
@ApiParam(value = "监控类型名称", example = "api") @PathVariable("app") String app) {
Job define = appService.getAppDefine(app.toLowerCase());
return ResponseEntity.ok(new Message<>(define));
}
@GetMapping(path = "/hierarchy") @GetMapping(path = "/hierarchy")
@ApiOperation(value = "查询全部层级的监控类型", notes = "查询所有监控类型,以层级结构输出") @ApiOperation(value = "查询全部层级的监控类型", notes = "查询所有监控类型,以层级结构输出")
public ResponseEntity<Message<List<Hierarchy>>> queryAppsHierarchy( public ResponseEntity<Message<List<Hierarchy>>> queryAppsHierarchy(

View File

@@ -31,7 +31,7 @@ CREATE TABLE param
id bigint not null auto_increment comment '参数ID', id bigint not null auto_increment comment '参数ID',
monitor_id bigint not null comment '监控ID', monitor_id bigint not null comment '监控ID',
field varchar(100) not null comment '参数标识符', field varchar(100) not null comment '参数标识符',
value varchar(255) not null comment '参数值,最大字符长度255', value varchar(255) comment '参数值,最大字符长度255',
type tinyint not null default 0 comment '参数类型 0:数字 1:字符串 2:加密串', type tinyint not null default 0 comment '参数类型 0:数字 1:字符串 2:加密串',
gmt_create timestamp default current_timestamp comment 'create time', gmt_create timestamp default current_timestamp comment 'create time',
gmt_update datetime default current_timestamp on update current_timestamp comment 'update time', gmt_update datetime default current_timestamp on update current_timestamp comment 'update time',

View File

@@ -1,49 +1,9 @@
<nz-card *ngIf="isTable" nzHoverable style="height: auto; margin-left: 14px" [nzBordered]="true" [nzTitle]="monitor_metrics_card_title"> <div
<nz-table #smallTable nzSize="small" nzNoResult="No Metrics Data" nzFrontPagination="false" [nzData]="valueRows"> echarts
<thead> [options]="eChartOption"
<tr> theme="default"
<th style="text-align: center" *ngFor="let field of fields">{{ field.name }}</th> [autoResize]="true"
</tr> [loading]="loading"
</thead> (chartInit)="onChartInit($event)"
<tbody> style="width: 500px; height: 400px; margin-left: 20px"
<tr *ngFor="let valueRow of smallTable.data"> ></div>
<td *ngFor="let value of valueRow.values">{{ value.origin }}</td>
</tr>
</tbody>
</nz-table>
</nz-card>
<nz-card *ngIf="!isTable" nzHoverable style="height: auto; margin-left: 14px" [nzBordered]="true" [nzTitle]="monitor_metrics_card_title">
<nz-table #smallTable nzSize="small" nzNoResult="No Metrics Data" nzFrontPagination="false" [nzData]="valueRows">
<thead>
<tr>
<th style="text-align: center">属性</th>
<th style="text-align: center"></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let field of fields; let i = index">
<td>{{ field.name }}</td>
<td>{{ rowValues[i].origin + ' ' + (field.unit ? field.unit : '') }}</td>
</tr>
</tbody>
</nz-table>
</nz-card>
<ng-template #monitor_metrics_card_title>
<p style="font-size: small; text-align: center; margin-bottom: 3px; color: #0c0c0c">{{ metrics }}</p>
<div>
<a nz-popover [nzPopoverContent]="'最近采集时间 ' + (time | _date: 'yyyy-MM-dd HH:mm:ss')">
<i nz-icon nzType="field-time" nzTheme="outline"></i
></a>
<i style="font-size: 0.3px; font-weight: normal; color: rgba(84, 83, 83, 0.89)">采集时间:{{ time | _date: 'HH:mm:ss' }}</i>
</div>
</ng-template>
<!--<nz-card *ngIf="!isTable" nzHoverable style="height:auto;margin-left: 14px;" [nzBordered]="true"-->
<!-- [nzTitle]="monitor_metrics_card_title" >-->
<!-- <div *ngFor="let field of fields;let i = index;" nz-row nzGutter="16">-->
<!-- <div nz-col nzSpan="10"><p style="text-align: right">{{field.name}}</p></div>-->
<!-- <div nz-col nzSpan="14"><p style="text-align: left">{{rowValues[i].origin}}</p></div>-->
<!-- </div>-->
<!--</nz-card>-->

View File

@@ -9,7 +9,8 @@ describe('MonitorDataChartComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ MonitorDataChartComponent ] declarations: [ MonitorDataChartComponent ]
}).compileComponents(); })
.compileComponents();
}); });
beforeEach(() => { beforeEach(() => {

View File

@@ -1,11 +1,13 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { EChartsOption } from 'echarts';
import { finalize } from 'rxjs/operators';
import { MonitorService } from '../../../service/monitor.service'; import { MonitorService } from '../../../service/monitor.service';
@Component({ @Component({
selector: 'app-monitor-data-chart', selector: 'app-monitor-data-chart',
templateUrl: './monitor-data-chart.component.html', templateUrl: './monitor-data-chart.component.html',
styleUrls: ['./monitor-data-chart.component.less'] styles: []
}) })
export class MonitorDataChartComponent implements OnInit { export class MonitorDataChartComponent implements OnInit {
@Input() @Input()
@@ -14,41 +16,254 @@ export class MonitorDataChartComponent implements OnInit {
} }
set monitorId(monitorId: number) { set monitorId(monitorId: number) {
this._monitorId = monitorId; this._monitorId = monitorId;
this.loadData();
} }
private _monitorId!: number; private _monitorId!: number;
@Input() @Input()
app!: string;
@Input()
metrics!: string; metrics!: string;
@Input()
time!: any; metric!: string;
fields!: any[]; @Input()
valueRows!: any[]; unit!: string;
rowValues!: any[]; eChartOption!: EChartsOption;
isTable: boolean = true; lineHistoryTheme!: EChartsOption;
loading: boolean = true;
echartsInstance!: any;
// 查询历史数据时间段 默认最近6小时
timePeriod: string = '6h';
constructor(private monitorSvc: MonitorService) {} constructor(private monitorSvc: MonitorService) {}
ngOnInit(): void {}
loadData() { ngOnInit(): void {
// 读取实时指标数据 this.lineHistoryTheme = {
let metricData$ = this.monitorSvc.getMonitorMetricsData(this.monitorId, this.metrics).subscribe( title: {
message => { text: `${this.metrics}.${this.metric}`,
metricData$.unsubscribe(); textStyle: {
if (message.code === 0) { fontSize: 16,
this.time = message.data.time; fontFamily: 'monospace',
this.fields = message.data.fields; textShadowOffsetX: 10
this.valueRows = message.data.valueRows;
if (this.valueRows.length == 1) {
this.isTable = false;
this.rowValues = this.valueRows[0].values;
} }
},
toolbox: {
show: true,
orient: 'vertical',
feature: {
dataZoom: {
yAxisIndex: 'none',
title: {
zoom: '区域缩放',
back: '缩放还原'
},
emphasis: {
iconStyle: {
textPosition: 'left'
}
}
},
saveAsImage: {
title: '保存图片',
emphasis: {
iconStyle: {
textPosition: 'left'
}
}
},
myPeriod1h: {
show: true,
title: '查询近1小时',
icon: 'path://M827.871087 196.128913C743.498468 111.756293 631.321596 65.290005 512 65.290005c-119.319549 0-231.499491 46.465265-315.871087 130.837884S65.290005 392.680451 65.290005 512s46.465265 231.499491 130.837884 315.871087 196.551538 130.837884 315.871087 130.837884c119.321596 0 231.499491-46.465265 315.871087-130.837884S958.708971 631.319549 958.708971 512 912.243707 280.500509 827.871087 196.128913zM531.556405 917.246651l0-74.145697c0-11.31572-9.174963-20.491707-20.491707-20.491707-11.316743 0-20.491707 9.174963-20.491707 20.491707l0 74.059739C283.276738 906.322857 116.693746 739.164766 106.755396 531.634176l72.351841 0c11.31572 0 20.491707-9.174963 20.491707-20.491707 0-11.31572-9.174963-20.491707-20.491707-20.491707l-72.273047 0c10.769274-206.737528 177.01253-373.005342 383.740848-383.813502l0 72.346725c0 11.316743 9.174963 20.491707 20.491707 20.491707 11.31572 0 20.491707-9.17394 20.491707-20.491707L531.558451 106.752326c207.593012 9.901511 374.807385 176.539762 385.609405 383.89946l-74.142627 0c-11.316743 0-20.491707 9.174963-20.491707 20.491707 0 11.316743 9.174963 20.491707 20.491707 20.491707l74.220399 0C907.275555 739.78796 739.720422 907.317511 531.556405 917.246651z;M532.098757 503.118726 532.098757 258.240529c0-11.316743-9.174963-20.491707-20.491707-20.491707-11.31572 0-20.491707 9.17394-20.491707 20.491707l0 254.66612c0 7.858992 4.429893 14.677281 10.924817 18.114566L693.447539 722.42757c4.002151 4.000104 9.245572 6.001691 14.490016 6.001691s10.487865-2.001587 14.490016-6.001691c8.002254-8.002254 8.002254-20.977777 0-28.980032L532.098757 503.118726z',
emphasis: {
iconStyle: {
textPosition: 'left'
}
},
onclick: () => {
this.loadData('1h');
}
},
myPeriod6h: {
show: true,
title: '查询近6小时',
icon: 'path://M827.871087 196.128913C743.498468 111.756293 631.321596 65.290005 512 65.290005c-119.319549 0-231.499491 46.465265-315.871087 130.837884S65.290005 392.680451 65.290005 512s46.465265 231.499491 130.837884 315.871087 196.551538 130.837884 315.871087 130.837884c119.321596 0 231.499491-46.465265 315.871087-130.837884S958.708971 631.319549 958.708971 512 912.243707 280.500509 827.871087 196.128913zM531.556405 917.246651l0-74.145697c0-11.31572-9.174963-20.491707-20.491707-20.491707-11.316743 0-20.491707 9.174963-20.491707 20.491707l0 74.059739C283.276738 906.322857 116.693746 739.164766 106.755396 531.634176l72.351841 0c11.31572 0 20.491707-9.174963 20.491707-20.491707 0-11.31572-9.174963-20.491707-20.491707-20.491707l-72.273047 0c10.769274-206.737528 177.01253-373.005342 383.740848-383.813502l0 72.346725c0 11.316743 9.174963 20.491707 20.491707 20.491707 11.31572 0 20.491707-9.17394 20.491707-20.491707L531.558451 106.752326c207.593012 9.901511 374.807385 176.539762 385.609405 383.89946l-74.142627 0c-11.316743 0-20.491707 9.174963-20.491707 20.491707 0 11.316743 9.174963 20.491707 20.491707 20.491707l74.220399 0C907.275555 739.78796 739.720422 907.317511 531.556405 917.246651z;M532.098757 503.118726 532.098757 258.240529c0-11.316743-9.174963-20.491707-20.491707-20.491707-11.31572 0-20.491707 9.17394-20.491707 20.491707l0 254.66612c0 7.858992 4.429893 14.677281 10.924817 18.114566L693.447539 722.42757c4.002151 4.000104 9.245572 6.001691 14.490016 6.001691s10.487865-2.001587 14.490016-6.001691c8.002254-8.002254 8.002254-20.977777 0-28.980032L532.098757 503.118726z',
emphasis: {
iconStyle: {
textPosition: 'left'
}
},
onclick: () => {
this.loadData('6h');
}
},
myPeriod1d: {
show: true,
title: '查询近1天',
icon: 'path://M827.871087 196.128913C743.498468 111.756293 631.321596 65.290005 512 65.290005c-119.319549 0-231.499491 46.465265-315.871087 130.837884S65.290005 392.680451 65.290005 512s46.465265 231.499491 130.837884 315.871087 196.551538 130.837884 315.871087 130.837884c119.321596 0 231.499491-46.465265 315.871087-130.837884S958.708971 631.319549 958.708971 512 912.243707 280.500509 827.871087 196.128913zM531.556405 917.246651l0-74.145697c0-11.31572-9.174963-20.491707-20.491707-20.491707-11.316743 0-20.491707 9.174963-20.491707 20.491707l0 74.059739C283.276738 906.322857 116.693746 739.164766 106.755396 531.634176l72.351841 0c11.31572 0 20.491707-9.174963 20.491707-20.491707 0-11.31572-9.174963-20.491707-20.491707-20.491707l-72.273047 0c10.769274-206.737528 177.01253-373.005342 383.740848-383.813502l0 72.346725c0 11.316743 9.174963 20.491707 20.491707 20.491707 11.31572 0 20.491707-9.17394 20.491707-20.491707L531.558451 106.752326c207.593012 9.901511 374.807385 176.539762 385.609405 383.89946l-74.142627 0c-11.316743 0-20.491707 9.174963-20.491707 20.491707 0 11.316743 9.174963 20.491707 20.491707 20.491707l74.220399 0C907.275555 739.78796 739.720422 907.317511 531.556405 917.246651z;M532.098757 503.118726 532.098757 258.240529c0-11.316743-9.174963-20.491707-20.491707-20.491707-11.31572 0-20.491707 9.17394-20.491707 20.491707l0 254.66612c0 7.858992 4.429893 14.677281 10.924817 18.114566L693.447539 722.42757c4.002151 4.000104 9.245572 6.001691 14.490016 6.001691s10.487865-2.001587 14.490016-6.001691c8.002254-8.002254 8.002254-20.977777 0-28.980032L532.098757 503.118726z',
emphasis: {
iconStyle: {
textPosition: 'left'
}
},
onclick: () => {
this.loadData('1d');
}
},
myPeriod1w: {
show: true,
title: '查询近1周',
icon: 'path://M827.871087 196.128913C743.498468 111.756293 631.321596 65.290005 512 65.290005c-119.319549 0-231.499491 46.465265-315.871087 130.837884S65.290005 392.680451 65.290005 512s46.465265 231.499491 130.837884 315.871087 196.551538 130.837884 315.871087 130.837884c119.321596 0 231.499491-46.465265 315.871087-130.837884S958.708971 631.319549 958.708971 512 912.243707 280.500509 827.871087 196.128913zM531.556405 917.246651l0-74.145697c0-11.31572-9.174963-20.491707-20.491707-20.491707-11.316743 0-20.491707 9.174963-20.491707 20.491707l0 74.059739C283.276738 906.322857 116.693746 739.164766 106.755396 531.634176l72.351841 0c11.31572 0 20.491707-9.174963 20.491707-20.491707 0-11.31572-9.174963-20.491707-20.491707-20.491707l-72.273047 0c10.769274-206.737528 177.01253-373.005342 383.740848-383.813502l0 72.346725c0 11.316743 9.174963 20.491707 20.491707 20.491707 11.31572 0 20.491707-9.17394 20.491707-20.491707L531.558451 106.752326c207.593012 9.901511 374.807385 176.539762 385.609405 383.89946l-74.142627 0c-11.316743 0-20.491707 9.174963-20.491707 20.491707 0 11.316743 9.174963 20.491707 20.491707 20.491707l74.220399 0C907.275555 739.78796 739.720422 907.317511 531.556405 917.246651z;M532.098757 503.118726 532.098757 258.240529c0-11.316743-9.174963-20.491707-20.491707-20.491707-11.31572 0-20.491707 9.17394-20.491707 20.491707l0 254.66612c0 7.858992 4.429893 14.677281 10.924817 18.114566L693.447539 722.42757c4.002151 4.000104 9.245572 6.001691 14.490016 6.001691s10.487865-2.001587 14.490016-6.001691c8.002254-8.002254 8.002254-20.977777 0-28.980032L532.098757 503.118726z',
emphasis: {
iconStyle: {
textPosition: 'left'
}
},
onclick: () => {
this.loadData('1w');
}
},
myPeriod4w: {
show: true,
title: '查询近1月',
icon: 'path://M827.871087 196.128913C743.498468 111.756293 631.321596 65.290005 512 65.290005c-119.319549 0-231.499491 46.465265-315.871087 130.837884S65.290005 392.680451 65.290005 512s46.465265 231.499491 130.837884 315.871087 196.551538 130.837884 315.871087 130.837884c119.321596 0 231.499491-46.465265 315.871087-130.837884S958.708971 631.319549 958.708971 512 912.243707 280.500509 827.871087 196.128913zM531.556405 917.246651l0-74.145697c0-11.31572-9.174963-20.491707-20.491707-20.491707-11.316743 0-20.491707 9.174963-20.491707 20.491707l0 74.059739C283.276738 906.322857 116.693746 739.164766 106.755396 531.634176l72.351841 0c11.31572 0 20.491707-9.174963 20.491707-20.491707 0-11.31572-9.174963-20.491707-20.491707-20.491707l-72.273047 0c10.769274-206.737528 177.01253-373.005342 383.740848-383.813502l0 72.346725c0 11.316743 9.174963 20.491707 20.491707 20.491707 11.31572 0 20.491707-9.17394 20.491707-20.491707L531.558451 106.752326c207.593012 9.901511 374.807385 176.539762 385.609405 383.89946l-74.142627 0c-11.316743 0-20.491707 9.174963-20.491707 20.491707 0 11.316743 9.174963 20.491707 20.491707 20.491707l74.220399 0C907.275555 739.78796 739.720422 907.317511 531.556405 917.246651z;M532.098757 503.118726 532.098757 258.240529c0-11.316743-9.174963-20.491707-20.491707-20.491707-11.31572 0-20.491707 9.17394-20.491707 20.491707l0 254.66612c0 7.858992 4.429893 14.677281 10.924817 18.114566L693.447539 722.42757c4.002151 4.000104 9.245572 6.001691 14.490016 6.001691s10.487865-2.001587 14.490016-6.001691c8.002254-8.002254 8.002254-20.977777 0-28.980032L532.098757 503.118726z',
emphasis: {
iconStyle: {
textPosition: 'left'
}
},
onclick: () => {
this.loadData('4w');
}
},
myRefresh: {
show: true,
title: '刷新',
icon: 'path://M663.881 339.763l274.021-0.742 0.058-13.271 0.699 0c-0.204-0.48-0.495-0.945-0.699-1.426L938.658 65l-23.776 0.044L914.3 280.41C835.9 151.374 694.321 65 532.342 65c-246.869 0-447 200.132-447 447 0 246.84 200.131 447 447 447 180.343 0 335.657-106.919 406.316-260.75l-33.176 0C836.948 835.027 695.456 929.2 532.342 929.2c-230.048 0-417.2-187.152-417.2-417.2s187.152-417.2 417.2-417.2c158.895 0 297.068 89.487 367.466 220.547l-235.868 0.64L663.881 339.763z',
emphasis: {
iconStyle: {
textPosition: 'left'
}
},
onclick: () => {
this.loadData();
}
}
}
},
tooltip: {
trigger: 'axis',
formatter: function (params: any) {
let time: number = params[0].value[0];
var date = new Date(time);
let seriesName = params[0].seriesName;
let month = (date.getMonth() + 1).toString().padStart(2, '0');
let day = date.getDate().toString().padStart(2, '0');
if (seriesName == null || seriesName == 'NULL') {
return `${date.getFullYear()}/${month}/${day} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()} -- ${
params[0].value[1]
}`;
} else { } else {
console.error(message.msg); return `${seriesName} ${date.getFullYear()}/${month}/${day} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()} -- ${
params[0].value[1]
}`;
}
}
},
xAxis: {
type: 'time',
splitLine: {
show: false
}
},
yAxis: {
type: 'value',
boundaryGap: [0, '100%'],
splitLine: {
show: true
}
}
};
if (this.unit != undefined || this.unit != null) {
// @ts-ignore
this.lineHistoryTheme.title?.subtext = `单位 ${this.unit}`;
}
this.loadData();
}
loadData(timePeriod?: string) {
if (timePeriod != undefined) {
this.timePeriod = timePeriod;
}
// 读取指标历史数据
this.loading = true;
let metricData$ = this.monitorSvc
.getMonitorMetricHistoryData(this.monitorId, this.app, this.metrics, this.metric, this.timePeriod, false)
.pipe(
finalize(() => {
this.loading = false;
metricData$.unsubscribe();
})
)
.subscribe(
message => {
if (message.code === 0 && message.data.values != undefined) {
let values: Record<string, any> = message.data.values;
let legend: string[] = [];
Object.keys(values).forEach(key => {
legend.push(key);
});
if (legend.length > 1) {
this.lineHistoryTheme.legend = {
orient: 'vertical',
data: legend
};
}
Object.keys(values).forEach(key => {
let seriesData: Array<{ value: any }> = [];
values[key].forEach((item: { time: number; origin: any }) => {
seriesData.push({
value: [item.time, item.origin]
});
});
this.lineHistoryTheme.series = [];
this.lineHistoryTheme.series.push({
name: key,
type: 'line',
smooth: true,
showSymbol: false,
data: seriesData
});
this.eChartOption = this.lineHistoryTheme;
if (this.echartsInstance != undefined) {
this.echartsInstance.setOption(this.eChartOption, {
replaceMerge: ['xAxis', 'yAxis', 'series']
});
}
});
} else {
this.eChartOption = this.lineHistoryTheme;
this.eChartOption.title = {
text: `${this.metrics}.${this.metric}` + '\n\n\n' + '暂无数据',
textStyle: {
fontSize: 16,
fontFamily: 'monospace',
textShadowOffsetX: 10
},
left: 'center',
top: 'center'
};
if (this.echartsInstance != undefined) {
this.echartsInstance.setOption(this.eChartOption, {
replaceMerge: ['title']
});
}
} }
}, },
error => { error => {
metricData$.unsubscribe(); console.error(error.msg);
} }
); );
} }
onChartInit(ec: any) {
this.echartsInstance = ec;
}
} }

View File

@@ -0,0 +1,49 @@
<nz-card *ngIf="isTable" nzHoverable style="height: auto; margin-left: 14px" [nzBordered]="true" [nzTitle]="monitor_metrics_card_title">
<nz-table #smallTable nzSize="small" nzNoResult="No Metrics Data" nzFrontPagination="false" [nzData]="valueRows">
<thead>
<tr>
<th style="text-align: center" *ngFor="let field of fields">{{ field.name }}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let valueRow of smallTable.data">
<td *ngFor="let value of valueRow.values">{{ value.origin }}</td>
</tr>
</tbody>
</nz-table>
</nz-card>
<nz-card *ngIf="!isTable" nzHoverable style="height: auto; margin-left: 14px" [nzBordered]="true" [nzTitle]="monitor_metrics_card_title">
<nz-table #smallTable nzSize="small" nzNoResult="No Metrics Data" nzFrontPagination="false" [nzData]="valueRows">
<thead>
<tr>
<th style="text-align: center">属性</th>
<th style="text-align: center"></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let field of fields; let i = index">
<td>{{ field.name }}</td>
<td>{{ rowValues[i].origin + ' ' + (field.unit ? field.unit : '') }}</td>
</tr>
</tbody>
</nz-table>
</nz-card>
<ng-template #monitor_metrics_card_title>
<p style="font-size: small; text-align: center; margin-bottom: 3px; color: #0c0c0c">{{ metrics }}</p>
<div>
<a nz-popover [nzPopoverContent]="'最近采集时间 ' + (time | _date: 'yyyy-MM-dd HH:mm:ss')">
<i nz-icon nzType="field-time" nzTheme="outline"></i
></a>
<i style="font-size: 0.3px; font-weight: normal; color: rgba(84, 83, 83, 0.89)">采集时间:{{ time | _date: 'HH:mm:ss' }}</i>
</div>
</ng-template>
<!--<nz-card *ngIf="!isTable" nzHoverable style="height:auto;margin-left: 14px;" [nzBordered]="true"-->
<!-- [nzTitle]="monitor_metrics_card_title" >-->
<!-- <div *ngFor="let field of fields;let i = index;" nz-row nzGutter="16">-->
<!-- <div nz-col nzSpan="10"><p style="text-align: right">{{field.name}}</p></div>-->
<!-- <div nz-col nzSpan="14"><p style="text-align: left">{{rowValues[i].origin}}</p></div>-->
<!-- </div>-->
<!--</nz-card>-->

View File

@@ -0,0 +1,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MonitorDataTableComponent } from './monitor-data-table.component';
describe('MonitorDataChartComponent', () => {
let component: MonitorDataTableComponent;
let fixture: ComponentFixture<MonitorDataTableComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [MonitorDataTableComponent]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(MonitorDataTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,55 @@
import { Component, Input } from '@angular/core';
import { MonitorService } from '../../../service/monitor.service';
@Component({
selector: 'app-monitor-data-table',
templateUrl: './monitor-data-table.component.html',
styleUrls: ['./monitor-data-table.component.less']
})
export class MonitorDataTableComponent {
@Input()
get monitorId(): number {
return this._monitorId;
}
set monitorId(monitorId: number) {
this._monitorId = monitorId;
// 需将monitorId作为输入参数的最后一个 这样在执行loadData时其它入参才有值
this.loadData();
}
private _monitorId!: number;
@Input()
metrics!: string;
time!: any;
fields!: any[];
valueRows!: any[];
rowValues!: any[];
isTable: boolean = true;
constructor(private monitorSvc: MonitorService) {}
loadData() {
// 读取实时指标数据
let metricData$ = this.monitorSvc.getMonitorMetricsData(this.monitorId, this.metrics).subscribe(
message => {
metricData$.unsubscribe();
if (message.code === 0) {
this.time = message.data.time;
this.fields = message.data.fields;
this.valueRows = message.data.valueRows;
if (this.valueRows.length == 1) {
this.isTable = false;
this.rowValues = this.valueRows[0].values;
}
} else {
console.error(message.msg);
}
},
error => {
console.error(error.msg);
metricData$.unsubscribe();
}
);
}
}

View File

@@ -103,11 +103,28 @@
<nz-tab [nzTitle]="titleTemplate"> <nz-tab [nzTitle]="titleTemplate">
<ng-template #titleTemplate> <ng-template #titleTemplate>
<i nz-icon nzType="pic-right" style="margin-left: 10px"></i> <i nz-icon nzType="pic-right" style="margin-left: 10px"></i>
监控数据报告详情 监控实时数据详情
</ng-template> </ng-template>
<div style="display: flex; justify-content: flex-start; flex-wrap: wrap"> <div style="display: flex; justify-content: flex-start; flex-wrap: wrap">
<div *ngFor="let metric of metrics; let i = index"> <div *ngFor="let metric of metrics; let i = index">
<app-monitor-data-chart [metrics]="metric" [monitorId]="monitorId"></app-monitor-data-chart> <app-monitor-data-table [metrics]="metric" [monitorId]="monitorId"></app-monitor-data-table>
</div>
</div>
</nz-tab>
<nz-tab [nzTitle]="title2Template" (nzClick)="initMetricChart()">
<ng-template #title2Template>
<i nz-icon nzType="pic-right" style="margin-left: 10px"></i>
监控历史图表详情
</ng-template>
<div style="display: flex; justify-content: flex-start; flex-wrap: wrap">
<div *ngFor="let item of chartMetrics; let i = index">
<app-monitor-data-chart
[app]="app"
[metrics]="item.metrics"
[metric]="item.metric"
[unit]="item.unit"
[monitorId]="monitorId"
></app-monitor-data-chart>
</div> </div>
</div> </div>
</nz-tab> </nz-tab>

View File

@@ -1,10 +1,10 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { ActivatedRoute, ParamMap } from '@angular/router';
import { TitleService } from '@delon/theme'; import { finalize, switchMap } from 'rxjs/operators';
import { switchMap } from 'rxjs/operators';
import { Monitor } from '../../../pojo/Monitor'; import { Monitor } from '../../../pojo/Monitor';
import { Param } from '../../../pojo/Param'; import { Param } from '../../../pojo/Param';
import { AppDefineService } from '../../../service/app-define.service';
import { MonitorService } from '../../../service/monitor.service'; import { MonitorService } from '../../../service/monitor.service';
@Component({ @Component({
@@ -13,14 +13,15 @@ import { MonitorService } from '../../../service/monitor.service';
styleUrls: ['./monitor-detail.component.less'] styleUrls: ['./monitor-detail.component.less']
}) })
export class MonitorDetailComponent implements OnInit { export class MonitorDetailComponent implements OnInit {
constructor(private monitorSvc: MonitorService, private route: ActivatedRoute, private router: Router, private titleSvc: TitleService) {} constructor(private monitorSvc: MonitorService, private route: ActivatedRoute, private appDefineSvc: AppDefineService) {}
isSpinning: boolean = false; isSpinning: boolean = false;
monitorId!: number; monitorId!: number;
app: string | undefined; app!: string;
monitor: Monitor | undefined; monitor!: Monitor;
options: any; options: any;
port: number | undefined; port: number | undefined;
metrics!: string[]; metrics!: string[];
chartMetrics: any[] = [];
ngOnInit(): void { ngOnInit(): void {
this.route.paramMap this.route.paramMap
@@ -53,6 +54,45 @@ export class MonitorDetailComponent implements OnInit {
}, },
error => { error => {
this.isSpinning = false; this.isSpinning = false;
console.error(error.msg);
}
);
}
initMetricChart() {
// 查询过滤出此监控下可计算聚合的数字指标
const define$ = this.appDefineSvc
.getAppDefine(this.app)
.pipe(
finalize(() => {
define$.unsubscribe();
})
)
.subscribe(
message => {
if (message.code === 0 && message.data != undefined) {
this.chartMetrics = [];
let metrics = message.data.metrics;
metrics.forEach((metric: { name: any; fields: any }) => {
let fields = metric.fields;
if (fields != undefined) {
fields.forEach((field: { type: number; field: any; unit: any }) => {
if (field.type == 0) {
this.chartMetrics.push({
metrics: metric.name,
metric: field.field,
unit: field.unit
});
}
});
}
});
} else {
console.warn(message.msg);
}
},
error => {
console.error(error.msg);
} }
); );
} }

View File

@@ -10,6 +10,7 @@ import { NzTagModule } from 'ng-zorro-antd/tag';
import { NgxEchartsModule } from 'ngx-echarts'; import { NgxEchartsModule } from 'ngx-echarts';
import { MonitorDataChartComponent } from './monitor-data-chart/monitor-data-chart.component'; import { MonitorDataChartComponent } from './monitor-data-chart/monitor-data-chart.component';
import { MonitorDataTableComponent } from './monitor-data-table/monitor-data-table.component';
import { MonitorDetailComponent } from './monitor-detail/monitor-detail.component'; import { MonitorDetailComponent } from './monitor-detail/monitor-detail.component';
import { MonitorEditComponent } from './monitor-edit/monitor-edit.component'; import { MonitorEditComponent } from './monitor-edit/monitor-edit.component';
import { MonitorListComponent } from './monitor-list/monitor-list.component'; import { MonitorListComponent } from './monitor-list/monitor-list.component';
@@ -21,6 +22,7 @@ const COMPONENTS: Array<Type<void>> = [
MonitorEditComponent, MonitorEditComponent,
MonitorListComponent, MonitorListComponent,
MonitorDetailComponent, MonitorDetailComponent,
MonitorDataTableComponent,
MonitorDataChartComponent MonitorDataChartComponent
]; ];

View File

@@ -21,6 +21,13 @@ export class AppDefineService {
return this.http.get<Message<ParamDefine[]>>(paramDefineUri); return this.http.get<Message<ParamDefine[]>>(paramDefineUri);
} }
public getAppDefine(app: string | undefined | null): Observable<Message<any>> {
if (app === null || app === undefined) {
console.log('getAppDefine app can not null');
}
return this.http.get<Message<any>>(`/apps/${app}/define`);
}
public getAppHierarchy(): Observable<Message<any>> { public getAppHierarchy(): Observable<Message<any>> {
let httpParams = new HttpParams().append('lang', 'zh-CN'); let httpParams = new HttpParams().append('lang', 'zh-CN');
const options = { params: httpParams }; const options = { params: httpParams };

View File

@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
const Authorization = 'Authorization'; const AuthorizationConst = 'Authorization';
const refreshToken = 'refresh-token'; const RefreshTokenConst = 'refresh-token';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@@ -19,23 +19,23 @@ export class LocalStorageService {
} }
public getAuthorizationToken(): string | null { public getAuthorizationToken(): string | null {
return this.getData(Authorization); return this.getData(AuthorizationConst);
} }
public getRefreshToken(): string | null { public getRefreshToken(): string | null {
return this.getData(refreshToken); return this.getData(RefreshTokenConst);
} }
public storageRefreshToken(token: string) { public storageRefreshToken(token: string) {
return this.putData(refreshToken, token); return this.putData(RefreshTokenConst, token);
} }
public storageAuthorizationToken(token: string) { public storageAuthorizationToken(token: string) {
return this.putData(Authorization, token); return this.putData(AuthorizationConst, token);
} }
public hasAuthorizationToken() { public hasAuthorizationToken() {
return localStorage.getItem(Authorization) != null; return localStorage.getItem(AuthorizationConst) != null;
} }
public clear() { public clear() {

View File

@@ -107,6 +107,24 @@ export class MonitorService {
return this.http.get<Message<any>>(`/monitor/${monitorId}/metrics/${metrics}`); return this.http.get<Message<any>>(`/monitor/${monitorId}/metrics/${metrics}`);
} }
public getMonitorMetricHistoryData(
monitorId: number,
app: string,
metrics: string,
metric: string,
history: string,
interval: boolean
): Observable<Message<any>> {
let metricFull = `${app}.${metrics}.${metric}`;
let httpParams = new HttpParams();
httpParams = httpParams.appendAll({
history: history,
interval: interval
});
const options = { params: httpParams };
return this.http.get<Message<any>>(`/monitor/${monitorId}/metric/${metricFull}`, options);
}
public getAppsMonitorSummary(): Observable<Message<any>> { public getAppsMonitorSummary(): Observable<Message<any>> {
return this.http.get<Message<any>>(summary_uri); return this.http.get<Message<any>>(summary_uri);
} }