[monitor]feature dashboard仪表盘重构 (#13)

This commit is contained in:
tomsun28
2022-03-06 21:48:00 +08:00
committed by GitHub
parent 0a3b077172
commit f324eeaa42
17 changed files with 710 additions and 25 deletions

View File

@@ -0,0 +1,9 @@
export class AppCount {
category!: string;
app!: string;
size: number = 0;
availableSize: number = 0;
unManageSize: number = 0;
unAvailableSize: number = 0;
unReachableSize: number = 0;
}

View File

@@ -1,3 +1,106 @@
<div nz-row nzGutter="16" style="margin-top: 70px">
<div nz-col nzXs="24" nzSm="12" nzMd="6" class="mb-md">
<div nz-row nzAlign="middle" class="bg-primary rounded-lg">
<div nz-col nzSpan="10" class="p-md text-white">
<div class="h2 mt0 font-weight-bold">{{ appCountService.size }}</div>
<p class="h5 text-nowrap mb0">
<i nz-icon nzType="cloud" nzTheme="outline"></i>
{{ 'monitor.category.service' | i18n }}
</p>
</div>
<div nz-col nzSpan="14" class="p-md text-white">
<nz-tag class="mb-xs">
<span>正常 </span><span style="font-weight: bolder">{{ appCountService.availableSize }}</span>
</nz-tag>
<nz-tag class="mb-xs">
<span>不可用 </span><span style="font-weight: bolder">{{ appCountService.unAvailableSize }}</span>
</nz-tag>
<nz-tag class="mb-xs">
<span>不可达 </span><span style="font-weight: bolder">{{ appCountService.unReachableSize }}</span>
</nz-tag>
<nz-tag class="mb-xs">
<span>未监控 </span><span style="font-weight: bolder">{{ appCountService.unManageSize }}</span>
</nz-tag>
</div>
</div>
</div>
<div nz-col nzXs="24" nzSm="12" nzMd="6" class="mb-md">
<div nz-row nzAlign="middle" class="bg-success rounded-lg">
<div nz-col nzSpan="10" class="p-md text-white">
<div class="h2 mt0 font-weight-bold">{{ appCountDb.size }}</div>
<p class="h5 text-nowrap mb0">
<i nz-icon nzType="database" nzTheme="outline"></i>
{{ 'monitor.category.db' | i18n }}
</p>
</div>
<div nz-col nzSpan="14">
<nz-tag class="mb-xs">
<span>正常 </span><span style="font-weight: bolder">{{ appCountDb.availableSize }}</span>
</nz-tag>
<nz-tag class="mb-xs">
<span>不可用 </span><span style="font-weight: bolder">{{ appCountDb.unAvailableSize }}</span>
</nz-tag>
<nz-tag class="mb-xs">
<span>不可达 </span><span style="font-weight: bolder">{{ appCountDb.unReachableSize }}</span>
</nz-tag>
<nz-tag class="mb-xs">
<span>未监控 </span><span style="font-weight: bolder">{{ appCountDb.unManageSize }}</span>
</nz-tag>
</div>
</div>
</div>
<div nz-col nzXs="24" nzSm="12" nzMd="6" class="mb-md">
<div nz-row nzAlign="middle" class="bg-orange rounded-lg">
<div nz-col nzSpan="10" class="p-md text-white">
<div class="h2 mt0 font-weight-bold">{{ appCountOs.size }}</div>
<p class="h5 text-nowrap mb0">
<i nz-icon nzType="windows" nzTheme="outline"></i>
{{ 'monitor.category.os' | i18n }}
</p>
</div>
<div nz-col nzSpan="14">
<nz-tag class="mb-xs">
<span>正常 </span><span style="font-weight: bolder">{{ appCountOs.availableSize }}</span>
</nz-tag>
<nz-tag class="mb-xs">
<span>不可用 </span><span style="font-weight: bolder">{{ appCountOs.unAvailableSize }}</span>
</nz-tag>
<nz-tag class="mb-xs">
<span>不可达 </span><span style="font-weight: bolder">{{ appCountOs.unReachableSize }}</span>
</nz-tag>
<nz-tag class="mb-xs">
<span>未监控 </span><span style="font-weight: bolder">{{ appCountOs.unManageSize }}</span>
</nz-tag>
</div>
</div>
</div>
<div nz-col nzXs="24" nzSm="12" nzMd="6" class="mb-md">
<div nz-row nzAlign="middle" class="bg-magenta rounded-lg">
<div nz-col nzSpan="10" class="p-md text-white">
<div class="h2 mt0 font-weight-bold">{{ appCountCustom.size }}</div>
<p class="h5 text-nowrap mb0">
<i nz-icon nzType="skin" nzTheme="outline"></i>
{{ 'monitor.category.custom' | i18n }}
</p>
</div>
<div nz-col nzSpan="14">
<nz-tag class="mb-xs">
<span>正常 </span><span style="font-weight: bolder">{{ appCountCustom.availableSize }}</span>
</nz-tag>
<nz-tag class="mb-xs">
<span>不可用 </span><span style="font-weight: bolder">{{ appCountCustom.unAvailableSize }}</span>
</nz-tag>
<nz-tag class="mb-xs">
<span>不可达 </span><span style="font-weight: bolder">{{ appCountCustom.unReachableSize }}</span>
</nz-tag>
<nz-tag class="mb-xs">
<span>未监控 </span><span style="font-weight: bolder">{{ appCountCustom.unManageSize }}</span>
</nz-tag>
</div>
</div>
</div>
</div>
<div
echarts
[options]="appsCountEChartOption"
@@ -5,6 +108,59 @@
[autoResize]="true"
[loading]="appsCountLoading"
(chartClick)="onChartClick($event)"
(chartInit)="onChartInit($event)"
style="width: 100%; height: 400px; margin-top: 5%"
(chartInit)="onAppsCountChartInit($event)"
style="width: 100%; height: 400px; margin-top: 1%"
></div>
<div nz-row nzGutter="16" style="margin-top: 10px">
<div nz-col nzXs="24" nzSm="24" nzMd="12" class="mb-md">
<nz-card nzHoverable nzTitle="最近告警列表" [nzExtra]="extraTemplate">
<nz-timeline nzMode="left">
<nz-timeline-item *ngFor="let alert of alerts; let i = index" [nzLabel]="alert.gmtCreate.toString()">
<p style="font-weight: 400">
<nz-tag *ngIf="alert.priority == 0" nzColor="red">
<i nz-icon nzType="bell" nzTheme="outline"></i>
<span>紧急告警</span>
</nz-tag>
<nz-tag *ngIf="alert.priority == 1" nzColor="orange">
<i nz-icon nzType="bell" nzTheme="outline"></i>
<span>严重告警</span>
</nz-tag>
<nz-tag *ngIf="alert.priority == 2" nzColor="yellow">
<i nz-icon nzType="bell" nzTheme="outline"></i>
<span>警告告警</span>
</nz-tag>
<span>[{{ alert.monitorName }}] </span>
{{ alert.content }}
</p>
</nz-timeline-item>
</nz-timeline>
</nz-card>
</div>
<div nz-col nzXs="24" nzSm="12" nzMd="7" class="mb-md">
<div
echarts
[options]="alertsEChartOption"
theme="default"
[autoResize]="true"
[loading]="alertsLoading"
(chartInit)="onAlertNumChartInit($event)"
style="width: 100%; height: 100%"
></div>
</div>
<div nz-col nzXs="24" nzSm="12" nzMd="5" class="mb-md">
<div
echarts
[options]="alertsDealEChartOption"
theme="default"
[autoResize]="true"
[loading]="alertsDealLoading"
(chartInit)="onAlertRateChartInit($event)"
style="width: 100%; height: 100%"
></div>
</div>
</div>
<ng-template #extraTemplate>
<a [routerLink]="['/alert/center']">进入告警中心</a>
</ng-template>

View File

@@ -1,3 +1,97 @@
.demo-chart {
height: auto;
@import '~@delon/theme/index';
:host ::ng-deep {
.ant-timeline {
.ant-timeline-label {
left: 20%;
width: calc(20% - 12px);
}
.ant-timeline-item-tail {
left: 20%;
}
.ant-timeline-item-head {
left: 20%;
}
.ant-timeline-item-label {
width: calc(20% - 12px);
}
.ant-timeline-item-left {
.ant-timeline-item-content {
left: calc(20% - 4px);
width: calc(80% - 14px);
}
}
}
.ant-card-head-title {
padding-top: 6px;
padding-right: 0;
padding-bottom: 6px;
padding-left: 0;
}
.ant-card-head {
min-height: 24px;
padding: 0 12px;
font-weight: 400;
font-size: 12px;
}
.ant-card-body {
padding-top: 24px;
padding-right: 24px;
padding-bottom: 6px;
padding-left: 24px;
}
.ant-timeline-item {
padding-bottom: 10px;
}
}
[data-theme='dark'] {
:host ::ng-deep {
.ant-timeline {
.ant-timeline-label {
left: 20%;
width: calc(20% - 12px);
}
.ant-timeline-item-tail {
left: 20%;
}
.ant-timeline-item-head {
left: 20%;
}
.ant-timeline-item-label {
width: calc(20% - 12px);
}
.ant-timeline-item-left {
.ant-timeline-item-content {
left: calc(20% - 4px);
width: calc(80% - 14px);
}
}
}
}
}
[data-theme='compact'] {
:host ::ng-deep {
.ant-timeline {
.ant-timeline-label {
left: 20%;
width: calc(20% - 12px);
}
.ant-timeline-item-tail {
left: 20%;
}
.ant-timeline-item-head {
left: 20%;
}
.ant-timeline-item-label {
width: calc(20% - 12px);
}
.ant-timeline-item-left {
.ant-timeline-item-content {
left: calc(20% - 4px);
width: calc(80% - 14px);
}
}
}
}
}

View File

@@ -6,6 +6,9 @@ import { EChartsOption } from 'echarts';
import { NzMessageService } from 'ng-zorro-antd/message';
import { fromEvent } from 'rxjs';
import { Alert } from '../../pojo/Alert';
import { AppCount } from '../../pojo/AppCount';
import { AlertService } from '../../service/alert.service';
import { MonitorService } from '../../service/monitor.service';
@Component({
@@ -18,21 +21,31 @@ export class DashboardComponent implements OnInit, OnDestroy {
constructor(
private msg: NzMessageService,
private monitorSvc: MonitorService,
private alertSvc: AlertService,
@Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService,
private router: Router,
private cdr: ChangeDetectorRef
) {}
// start 大类别数量信息
appCountService: AppCount = new AppCount();
appCountOs: AppCount = new AppCount();
appCountDb: AppCount = new AppCount();
appCountCustom: AppCount = new AppCount();
// start 数量全局概览
interval$!: number;
appsCountLoading: boolean = true;
appsCountTableData: any[] = [];
appsCountEChartOption!: EChartsOption;
appsCountTheme!: EChartsOption;
echartsInstance!: any;
appsCountEchartsInstance!: any;
pageResize$!: any;
// 告警列表
alerts!: Alert[];
ngOnInit(): void {
this.appsCountLoading = true;
this.appsCountTheme = {
title: {
text: '监控总览',
@@ -112,8 +125,86 @@ export class DashboardComponent implements OnInit, OnDestroy {
}
]
};
this.alertsTheme = {
title: {
subtext: '告警等级分布',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
xAxis: {
type: 'category',
data: ['警告告警', '严重告警', '紧急告警']
},
yAxis: {
type: 'value'
},
series: [
{
name: '告警数量',
type: 'bar',
data: [
{
value: 0,
// 设置单个柱子的样式
itemStyle: {
color: '#ffb72b',
shadowColor: '#91cc75'
}
},
{
value: 0,
itemStyle: {
color: '#fa6202',
shadowColor: '#91cc75'
}
},
{
value: 0,
itemStyle: {
color: '#dc1313',
shadowColor: '#91cc75'
}
}
]
}
]
};
this.alertsDealTheme = {
title: {
subtext: '告警处理',
left: 'center'
},
tooltip: {
formatter: '{b} : {c}%'
},
series: [
{
name: '告警处理率',
type: 'gauge',
progress: {
show: true
},
detail: {
valueAnimation: true,
formatter: '{value}'
},
data: [
{
value: 0,
name: '告警处理率'
}
]
}
]
};
this.appsCountLoading = true;
this.alertsLoading = true;
this.refresh();
this.appsCountLoading = false;
// https://stackoverflow.com/questions/43908009/why-is-setinterval-in-an-angular-service-only-firing-one-time
this.interval$ = setInterval(this.refresh.bind(this), 30000);
this.pageResize$ = fromEvent(window, 'resize').subscribe(event => {
@@ -129,12 +220,21 @@ export class DashboardComponent implements OnInit, OnDestroy {
}
refresh(): void {
this.refreshAppsCount();
this.refreshAlertContentList();
this.refreshAlertSummary();
}
refreshAppsCount(): void {
this.appCountService = new AppCount();
this.appCountOs = new AppCount();
this.appCountDb = new AppCount();
this.appCountCustom = new AppCount();
let dashboard$ = this.monitorSvc.getAppsMonitorSummary().subscribe(
message => {
dashboard$.unsubscribe();
if (message.code === 0 && message.data.apps != undefined) {
// {app:'linux',size: 12}
let apps: any[] = message.data.apps;
let apps: AppCount[] = message.data.apps;
this.appsCountTableData = [];
let total = 0;
apps.forEach(app => {
@@ -147,6 +247,36 @@ export class DashboardComponent implements OnInit, OnDestroy {
value: app.size
});
total = total + (app.size ? app.size : 0);
switch (app.category) {
case 'service':
this.appCountService.size += app.size;
this.appCountService.availableSize += app.availableSize;
this.appCountService.unAvailableSize += app.unAvailableSize;
this.appCountService.unManageSize += app.unManageSize;
this.appCountService.unReachableSize += app.unReachableSize;
break;
case 'db':
this.appCountDb.size += app.size;
this.appCountDb.availableSize += app.availableSize;
this.appCountDb.unAvailableSize += app.unAvailableSize;
this.appCountDb.unManageSize += app.unManageSize;
this.appCountDb.unReachableSize += app.unReachableSize;
break;
case 'os':
this.appCountOs.size += app.size;
this.appCountOs.availableSize += app.availableSize;
this.appCountOs.unAvailableSize += app.unAvailableSize;
this.appCountOs.unManageSize += app.unManageSize;
this.appCountOs.unReachableSize += app.unReachableSize;
break;
case 'custom':
this.appCountCustom.size += app.size;
this.appCountCustom.availableSize += app.availableSize;
this.appCountCustom.unAvailableSize += app.unAvailableSize;
this.appCountCustom.unManageSize += app.unManageSize;
this.appCountCustom.unReachableSize += app.unReachableSize;
break;
}
});
// @ts-ignore
this.appsCountTheme.series[0].data = [{ value: total, name: '监控总量' }];
@@ -158,9 +288,11 @@ export class DashboardComponent implements OnInit, OnDestroy {
this.appsCountEChartOption = this.appsCountTheme;
this.cdr.detectChanges();
}
this.appsCountLoading = false;
},
error => {
console.error(error);
this.appsCountLoading = false;
dashboard$.unsubscribe();
}
);
@@ -175,12 +307,113 @@ export class DashboardComponent implements OnInit, OnDestroy {
}
}
onChartInit(ec: any) {
this.echartsInstance = ec;
onAppsCountChartInit(ec: any) {
this.appsCountEchartsInstance = ec;
}
onAlertNumChartInit(ec: any) {
this.alertsEchartsInstance = ec;
}
onAlertRateChartInit(ec: any) {
this.alertsDealEchartsInstance = ec;
}
resizeChart() {
if (this.echartsInstance) {
this.echartsInstance.resize();
if (this.appsCountEchartsInstance) {
this.appsCountEchartsInstance.resize();
}
if (this.alertsEchartsInstance) {
this.alertsEchartsInstance.resize();
}
if (this.alertsDealEchartsInstance) {
this.alertsDealEchartsInstance.resize();
}
}
// start 告警分布
alertsEChartOption!: EChartsOption;
alertsTheme!: EChartsOption;
alertsEchartsInstance!: any;
alertsLoading: boolean = true;
refreshAlerts(): void {
this.alertsEChartOption = this.alertsTheme;
this.cdr.detectChanges();
this.alertsLoading = false;
}
// start 告警处理率
alertsDealEChartOption!: EChartsOption;
alertsDealTheme!: EChartsOption;
alertsDealEchartsInstance!: any;
alertsDealLoading: boolean = true;
refreshAlertContentList(): void {
let alertsInit$ = this.alertSvc.getAlerts(0, 4).subscribe(
message => {
if (message.code === 0) {
let page = message.data;
this.alerts = page.content;
} else {
console.warn(message.msg);
}
alertsInit$.unsubscribe();
},
error => {
alertsInit$.unsubscribe();
console.error(error.msg);
}
);
}
refreshAlertSummary(): void {
let alertSummaryInit$ = this.alertSvc.getAlertsSummary().subscribe(
message => {
if (message.code === 0) {
let summary = message.data;
// @ts-ignore
this.alertsTheme.series[0].data = [
{
value: summary.priorityWarningNum,
itemStyle: {
color: '#ffb72b',
shadowColor: '#91cc75'
}
},
{
value: summary.priorityCriticalNum,
itemStyle: {
color: '#fa6202',
shadowColor: '#91cc75'
}
},
{
value: summary.priorityEmergencyNum,
itemStyle: {
color: '#dc1313',
shadowColor: '#91cc75'
}
}
];
// @ts-ignore
this.alertsDealTheme.series[0].data = [
{
value: summary.rate,
name: '告警处理率'
}
];
this.alertsEChartOption = this.alertsTheme;
this.alertsDealEChartOption = this.alertsDealTheme;
this.cdr.detectChanges();
} else {
console.warn(message.msg);
}
alertSummaryInit$.unsubscribe();
},
error => {
alertSummaryInit$.unsubscribe();
this.alertsDealLoading = false;
this.alertsLoading = false;
console.error(error.msg);
}
);
}
}

View File

@@ -10,6 +10,8 @@ import { UserLockComponent } from './passport/lock/lock.component';
// passport pages
import { UserLoginComponent } from './passport/login/login.component';
import { RouteRoutingModule } from './routes-routing.module';
import { NzTagModule } from 'ng-zorro-antd/tag';
import { NzTimelineModule } from 'ng-zorro-antd/timeline';
const COMPONENTS: Array<Type<void>> = [
DashboardComponent,
@@ -20,7 +22,7 @@ const COMPONENTS: Array<Type<void>> = [
];
@NgModule({
imports: [SharedModule, RouteRoutingModule, NgxEchartsModule],
imports: [SharedModule, RouteRoutingModule, NgxEchartsModule, NzTagModule, NzTimelineModule],
declarations: COMPONENTS
})
export class RoutesModule {}

View File

@@ -7,7 +7,7 @@ import { Message } from '../pojo/Message';
import { Page } from '../pojo/Page';
const alerts_uri = '/alerts';
const alerts_summary_uri = '/alerts/summary';
const alerts_status_uri = '/alerts/status';
@Injectable({
@@ -82,4 +82,8 @@ export class AlertService {
const options = { params: httpParams };
return this.http.put<Message<any>>(`${alerts_status_uri}/${status}`, null, options);
}
public getAlertsSummary(): Observable<Message<any>> {
return this.http.get<Message<any>>(alerts_summary_uri);
}
}

View File

@@ -44,7 +44,7 @@
{
"key": "os",
"text": "操作系统",
"hide": true,
"hide": false,
"i18n": "menu.monitor.os",
"icon": "anticon-windows"
},

View File

@@ -44,7 +44,8 @@
"service": "应用服务",
"db": "数据库",
"os": "操作系统",
"mid": "中间件"
"mid": "中间件",
"custom": "自定义监控"
},
"app": {
"": "监控类型",