Просмотр исходного кода

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

tomsun28 4 лет назад
Родитель
Сommit
59cec31bcb

+ 10 - 1
manager/src/main/java/com/usthe/manager/controller/AppController.java

@@ -1,8 +1,9 @@
 package com.usthe.manager.controller;
 
 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.manager.pojo.dto.Hierarchy;
 import com.usthe.manager.service.AppService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -40,6 +41,14 @@ public class AppController {
         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")
     @ApiOperation(value = "查询全部层级的监控类型", notes = "查询所有监控类型,以层级结构输出")
     public ResponseEntity<Message<List<Hierarchy>>> queryAppsHierarchy(

+ 1 - 1
manager/src/main/resources/db/schema.sql

@@ -31,7 +31,7 @@ CREATE TABLE  param
     id           bigint       not null auto_increment comment '参数ID',
     monitor_id   bigint       not null comment '监控ID',
     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:加密串',
     gmt_create   timestamp    default current_timestamp comment 'create time',
     gmt_update   datetime     default current_timestamp on update current_timestamp comment 'update time',

+ 9 - 49
web-app/src/app/routes/monitor/monitor-data-chart/monitor-data-chart.component.html

@@ -1,49 +1,9 @@
-<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>-->
+<div
+  echarts
+  [options]="eChartOption"
+  theme="default"
+  [autoResize]="true"
+  [loading]="loading"
+  (chartInit)="onChartInit($event)"
+  style="width: 500px; height: 400px; margin-left: 20px"
+></div>

+ 3 - 2
web-app/src/app/routes/monitor/monitor-data-chart/monitor-data-chart.component.spec.ts

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

+ 242 - 27
web-app/src/app/routes/monitor/monitor-data-chart/monitor-data-chart.component.ts

@@ -1,11 +1,13 @@
 import { Component, Input, OnInit } from '@angular/core';
+import { EChartsOption } from 'echarts';
+import { finalize } from 'rxjs/operators';
 
 import { MonitorService } from '../../../service/monitor.service';
 
 @Component({
   selector: 'app-monitor-data-chart',
   templateUrl: './monitor-data-chart.component.html',
-  styleUrls: ['./monitor-data-chart.component.less']
+  styles: []
 })
 export class MonitorDataChartComponent implements OnInit {
   @Input()
@@ -14,41 +16,254 @@ export class MonitorDataChartComponent implements OnInit {
   }
   set monitorId(monitorId: number) {
     this._monitorId = monitorId;
-    this.loadData();
   }
   private _monitorId!: number;
   @Input()
+  app!: string;
+  @Input()
   metrics!: string;
-
-  time!: any;
-  fields!: any[];
-  valueRows!: any[];
-  rowValues!: any[];
-  isTable: boolean = true;
-
+  @Input()
+  metric!: string;
+  @Input()
+  unit!: string;
+  eChartOption!: EChartsOption;
+  lineHistoryTheme!: EChartsOption;
+  loading: boolean = true;
+  echartsInstance!: any;
+  // 查询历史数据时间段 默认最近6小时
+  timePeriod: string = '6h';
   constructor(private monitorSvc: MonitorService) {}
-  ngOnInit(): void {}
 
-  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;
+  ngOnInit(): void {
+    this.lineHistoryTheme = {
+      title: {
+        text: `${this.metrics}.${this.metric}`,
+        textStyle: {
+          fontSize: 16,
+          fontFamily: 'monospace',
+          textShadowOffsetX: 10
+        }
+      },
+      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 {
+            return `${seriesName} ${date.getFullYear()}/${month}/${day} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()} -- ${
+              params[0].value[1]
+            }`;
           }
-        } else {
-          console.error(message.msg);
         }
       },
-      error => {
-        metricData$.unsubscribe();
+      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 => {
+          console.error(error.msg);
+        }
+      );
+  }
+
+  onChartInit(ec: any) {
+    this.echartsInstance = ec;
   }
 }

+ 49 - 0
web-app/src/app/routes/monitor/monitor-data-table/monitor-data-table.component.html

@@ -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>-->

+ 0 - 0
web-app/src/app/routes/monitor/monitor-data-chart/monitor-data-chart.component.less → web-app/src/app/routes/monitor/monitor-data-table/monitor-data-table.component.less


+ 24 - 0
web-app/src/app/routes/monitor/monitor-data-table/monitor-data-table.component.spec.ts

@@ -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();
+  });
+});

+ 55 - 0
web-app/src/app/routes/monitor/monitor-data-table/monitor-data-table.component.ts

@@ -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();
+      }
+    );
+  }
+}

+ 19 - 2
web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.html

@@ -103,11 +103,28 @@
         <nz-tab [nzTitle]="titleTemplate">
           <ng-template #titleTemplate>
             <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 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>
         </nz-tab>

+ 46 - 6
web-app/src/app/routes/monitor/monitor-detail/monitor-detail.component.ts

@@ -1,10 +1,10 @@
 import { Component, OnInit } from '@angular/core';
-import { ActivatedRoute, ParamMap, Router } from '@angular/router';
-import { TitleService } from '@delon/theme';
-import { switchMap } from 'rxjs/operators';
+import { ActivatedRoute, ParamMap } from '@angular/router';
+import { finalize, switchMap } from 'rxjs/operators';
 
 import { Monitor } from '../../../pojo/Monitor';
 import { Param } from '../../../pojo/Param';
+import { AppDefineService } from '../../../service/app-define.service';
 import { MonitorService } from '../../../service/monitor.service';
 
 @Component({
@@ -13,14 +13,15 @@ import { MonitorService } from '../../../service/monitor.service';
   styleUrls: ['./monitor-detail.component.less']
 })
 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;
   monitorId!: number;
-  app: string | undefined;
-  monitor: Monitor | undefined;
+  app!: string;
+  monitor!: Monitor;
   options: any;
   port: number | undefined;
   metrics!: string[];
+  chartMetrics: any[] = [];
 
   ngOnInit(): void {
     this.route.paramMap
@@ -53,6 +54,45 @@ export class MonitorDetailComponent implements OnInit {
         },
         error => {
           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);
         }
       );
   }

+ 2 - 0
web-app/src/app/routes/monitor/monitor.module.ts

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

+ 7 - 0
web-app/src/app/service/app-define.service.ts

@@ -21,6 +21,13 @@ export class AppDefineService {
     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>> {
     let httpParams = new HttpParams().append('lang', 'zh-CN');
     const options = { params: httpParams };

+ 7 - 7
web-app/src/app/service/local-storage.service.ts

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

+ 18 - 0
web-app/src/app/service/monitor.service.ts

@@ -107,6 +107,24 @@ export class MonitorService {
     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>> {
     return this.http.get<Message<any>>(summary_uri);
   }