[monitor] 提供指标实时数据查询API,初始化监控详情页代码
This commit is contained in:
34
common/src/main/java/com/usthe/common/entity/dto/Field.java
Normal file
34
common/src/main/java/com/usthe/common/entity/dto/Field.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.usthe.common.entity.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 监控指标组指标字段
|
||||
* @author tom
|
||||
* @date 2021/12/5 17:29
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ApiModel(description = "监控指标组指标字段")
|
||||
public class Field {
|
||||
|
||||
@ApiModelProperty(value = "指标采集字符名称", position = 0)
|
||||
private String name;
|
||||
|
||||
@ApiModelProperty(value = "字段类型:0-number数字 1-string字符串", position = 1)
|
||||
private Byte type;
|
||||
|
||||
@ApiModelProperty(value = "指标单位", position = 2)
|
||||
private String unit;
|
||||
|
||||
@ApiModelProperty(value = "是否是实例字段", position = 3)
|
||||
private boolean instance;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.usthe.common.entity.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 指标组监控数据
|
||||
* @author tom
|
||||
* @date 2021/12/5 17:24
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ApiModel(description = "指标组监控数据")
|
||||
public class MetricsData {
|
||||
|
||||
@ApiModelProperty(value = "监控ID", position = 0)
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "监控类型", position = 1)
|
||||
private String app;
|
||||
|
||||
@ApiModelProperty(value = "监控指标组", position = 2)
|
||||
private String metric;
|
||||
|
||||
@ApiModelProperty(value = "最新采集时间", position = 3)
|
||||
private Long time;
|
||||
|
||||
@ApiModelProperty(value = "监控指标字段列表", position = 4)
|
||||
private List<Field> fields;
|
||||
|
||||
@ApiModelProperty(value = "监控指标列表值集合")
|
||||
private List<ValueRow> valueRows;
|
||||
}
|
||||
40
common/src/main/java/com/usthe/common/entity/dto/Value.java
Normal file
40
common/src/main/java/com/usthe/common/entity/dto/Value.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.usthe.common.entity.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 监控指标组指标值
|
||||
* @author tom
|
||||
* @date 2021/12/5 17:43
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ApiModel(description = "监控指标组指标值")
|
||||
public class Value {
|
||||
|
||||
public Value(String origin) {
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
@ApiModelProperty(value = "原始值", position = 0)
|
||||
private String origin;
|
||||
|
||||
@ApiModelProperty(value = "平均值", position = 1)
|
||||
private String mean;
|
||||
|
||||
@ApiModelProperty(value = "中位数值", position = 0)
|
||||
private String median;
|
||||
|
||||
@ApiModelProperty(value = "最小值", position = 0)
|
||||
private String min;
|
||||
|
||||
@ApiModelProperty(value = "最大值", position = 0)
|
||||
private String max;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.usthe.common.entity.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 监控指标组的一行指标数据
|
||||
* @author tom
|
||||
* @date 2021/12/5 17:39
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@ApiModel(description = "监控指标组的一行指标数据")
|
||||
public class ValueRow {
|
||||
|
||||
@ApiModelProperty(value = "此行数据唯一实例", position = 0)
|
||||
private String instance;
|
||||
|
||||
@ApiModelProperty(value = "监控指标组指标值", position = 1)
|
||||
private List<Value> values;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.usthe.warehouse.controller;
|
||||
|
||||
import com.usthe.common.entity.dto.Field;
|
||||
import com.usthe.common.entity.dto.Message;
|
||||
import com.usthe.common.entity.dto.MetricsData;
|
||||
import com.usthe.common.entity.dto.Value;
|
||||
import com.usthe.common.entity.dto.ValueRow;
|
||||
import com.usthe.common.entity.message.CollectRep;
|
||||
import com.usthe.warehouse.store.RedisDataStorage;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
|
||||
/**
|
||||
* 指标数据查询接口
|
||||
* @author tom
|
||||
* @date 2021/12/5 15:52
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(produces = {APPLICATION_JSON_VALUE})
|
||||
@Api(tags = "监控指标数据API")
|
||||
public class MetricsDataController {
|
||||
|
||||
@Autowired
|
||||
private RedisDataStorage redisDataStorage;
|
||||
|
||||
@GetMapping("/monitors/{monitorId}/metrics/{metric}")
|
||||
@ApiOperation(value = "查询监控指标组的指标数据", notes = "查询监控指标组的指标数据")
|
||||
public ResponseEntity<Message<MetricsData>> getMetricsData(
|
||||
@ApiParam(value = "监控ID", example = "343254354")
|
||||
@PathVariable Long monitorId,
|
||||
@ApiParam(value = "监控指标组", example = "cpu")
|
||||
@PathVariable String metric) {
|
||||
CollectRep.MetricsData redisData = redisDataStorage.getCurrentMetricsData(monitorId, metric);
|
||||
if (redisData == null) {
|
||||
return ResponseEntity.ok().body(new Message<>("query metrics data is empty"));
|
||||
}
|
||||
{
|
||||
MetricsData.MetricsDataBuilder dataBuilder = MetricsData.builder();
|
||||
dataBuilder.id(redisData.getId()).app(redisData.getApp()).metric(redisData.getMetrics())
|
||||
.time(redisData.getTime());
|
||||
List<Field> fields = redisData.getFieldsList().stream().map(redisField ->
|
||||
Field.builder().name(redisField.getName())
|
||||
.type(Integer.valueOf(redisField.getType()).byteValue()).build())
|
||||
.collect(Collectors.toList());
|
||||
dataBuilder.fields(fields);
|
||||
List<ValueRow> valueRows = redisData.getValuesList().stream().map(redisValueRow ->
|
||||
ValueRow.builder().instance(redisValueRow.getInstance())
|
||||
.values(redisValueRow.getColumnsList().stream().map(Value::new).collect(Collectors.toList()))
|
||||
.build()).collect(Collectors.toList());
|
||||
dataBuilder.valueRows(valueRows);
|
||||
return ResponseEntity.ok().body(new Message<>(dataBuilder.build()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/monitors/{monitorId}/metrics/{metric}/fields/{field}")
|
||||
@ApiOperation(value = "查询监控指标组的指定指标的历史数据", notes = "查询监控指标组下的指定指标的历史数据")
|
||||
public ResponseEntity<Message<Void>> getMetricHistoryData(
|
||||
@ApiParam(value = "监控ID", example = "343254354")
|
||||
@PathVariable Long monitorId,
|
||||
@ApiParam(value = "监控指标组", example = "cpu")
|
||||
@PathVariable String metric,
|
||||
@ApiParam(value = "监控指标组指标", example = "343254354")
|
||||
@PathVariable String field,
|
||||
@ApiParam(value = "查询历史时间段,默认6h-6小时:h-小时, d-天, m-月, y-年", example = "6h")
|
||||
@RequestParam(required = false) String history
|
||||
) {
|
||||
return ResponseEntity.ok().body(null);
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,13 @@ import io.lettuce.core.RedisClient;
|
||||
import io.lettuce.core.RedisURI;
|
||||
import io.lettuce.core.api.StatefulRedisConnection;
|
||||
import io.lettuce.core.api.async.RedisAsyncCommands;
|
||||
import io.lettuce.core.api.sync.RedisCommands;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
@@ -43,6 +45,11 @@ public class RedisDataStorage implements DisposableBean {
|
||||
startStorageData();
|
||||
}
|
||||
|
||||
public CollectRep.MetricsData getCurrentMetricsData(@NonNull Long monitorId, @NonNull String metric) {
|
||||
RedisCommands<String, CollectRep.MetricsData> commands = connection.sync();
|
||||
return commands.hget(String.valueOf(monitorId), metric);
|
||||
}
|
||||
|
||||
private void startStorageData() {
|
||||
Runnable runnable = () -> {
|
||||
Thread.currentThread().setName("warehouse-redis-data-storage");
|
||||
|
||||
@@ -4,4 +4,5 @@ com.usthe.warehouse.MetricsDataQueue,\
|
||||
com.usthe.warehouse.WarehouseWorkerPool,\
|
||||
com.usthe.warehouse.entrance.KafkaDataConsume,\
|
||||
com.usthe.warehouse.store.InfluxdbDataStorage,\
|
||||
com.usthe.warehouse.store.RedisDataStorage
|
||||
com.usthe.warehouse.store.RedisDataStorage,\
|
||||
com.usthe.warehouse.controller.MetricsDataController
|
||||
@@ -41,8 +41,10 @@
|
||||
"@delon/util": "^12.4.2",
|
||||
"ajv": "^8.6.2",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"echarts": "^5.2.2",
|
||||
"ng-alain": "^12.4.2",
|
||||
"ng-zorro-antd": "^12.0.2",
|
||||
"ngx-echarts": "^v7.1.0",
|
||||
"rxjs": "~6.6.0",
|
||||
"screenfull": "^5.1.0",
|
||||
"tslib": "^2.3.0",
|
||||
@@ -60,6 +62,7 @@
|
||||
"@angular/language-service": "~12.2.0",
|
||||
"@delon/testing": "^12.4.2",
|
||||
"@ngx-formly/schematics": "^5.10.23",
|
||||
"@types/echarts": "^4.9.12",
|
||||
"@types/jasmine": "~3.8.0",
|
||||
"@types/node": "^12.11.1",
|
||||
"@typescript-eslint/eslint-plugin": "~4.29.2",
|
||||
|
||||
@@ -84,6 +84,7 @@ import { RoutesModule } from './routes/routes.module';
|
||||
import { SharedModule } from './shared/shared.module';
|
||||
import { STWidgetModule } from './shared/st-widget/st-widget.module';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { NgxEchartsModule } from 'ngx-echarts';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -103,7 +104,10 @@ import { ReactiveFormsModule } from '@angular/forms';
|
||||
NzNotificationModule,
|
||||
...FORM_MODULES,
|
||||
...GLOBAL_THIRD_MODULES,
|
||||
ReactiveFormsModule
|
||||
ReactiveFormsModule,
|
||||
NgxEchartsModule.forRoot({
|
||||
echarts: () => import('echarts')
|
||||
})
|
||||
],
|
||||
providers: [
|
||||
...LANG_PROVIDES,
|
||||
|
||||
@@ -1 +1,21 @@
|
||||
<p>monitor-detail works!</p>
|
||||
<nz-divider></nz-divider>
|
||||
<nz-breadcrumb>
|
||||
<nz-breadcrumb-item>
|
||||
<a [routerLink]="['/']">
|
||||
<i nz-icon nzType="home"></i>
|
||||
</a>
|
||||
</nz-breadcrumb-item>
|
||||
<nz-breadcrumb-item>
|
||||
<a [routerLink]="['/monitors']" [queryParams]="{app: app ? app : ''}">
|
||||
<i nz-icon nzType="monitor"></i>
|
||||
<span>监控列表</span>
|
||||
</a>
|
||||
</nz-breadcrumb-item>
|
||||
<nz-breadcrumb-item>
|
||||
<i nz-icon nzType="pie-chart"></i>
|
||||
<span>{{'monitor.app.' + app | i18n}} 监控详情</span>
|
||||
</nz-breadcrumb-item>
|
||||
</nz-breadcrumb>
|
||||
<nz-divider></nz-divider>
|
||||
|
||||
<div echarts [options]="options" class="demo-chart"></div>
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {MonitorService} from "../../../service/monitor.service";
|
||||
import {ActivatedRoute, ParamMap, Router} from "@angular/router";
|
||||
import {TitleService} from "@delon/theme";
|
||||
import {switchMap} from "rxjs/operators";
|
||||
import {Message} from "../../../pojo/Message";
|
||||
import {Param} from "../../../pojo/Param";
|
||||
import {throwError} from "rxjs";
|
||||
|
||||
@Component({
|
||||
selector: 'app-monitor-detail',
|
||||
@@ -8,9 +15,74 @@ import { Component, OnInit } from '@angular/core';
|
||||
})
|
||||
export class MonitorDetailComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
constructor(private monitorSvc: MonitorService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private titleSvc: TitleService) { }
|
||||
isSpinning: boolean = false
|
||||
monitorId!: number;
|
||||
app: string | undefined;
|
||||
|
||||
options: any;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.route.paramMap.pipe(
|
||||
switchMap((paramMap: ParamMap) => {
|
||||
this.isSpinning = false;
|
||||
let id = paramMap.get("monitorId");
|
||||
this.monitorId = Number(id);
|
||||
// 查询监控指标组结构信息
|
||||
return this.monitorSvc.getMonitor(this.monitorId);
|
||||
})
|
||||
).subscribe(message => {
|
||||
if (message.code === 0) {
|
||||
|
||||
} else {
|
||||
console.warn(message.msg);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const xAxisData = [];
|
||||
const data1 = [];
|
||||
const data2 = [];
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
xAxisData.push('category' + i);
|
||||
data1.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5);
|
||||
data2.push((Math.cos(i / 5) * (i / 5 - 10) + i / 6) * 5);
|
||||
}
|
||||
|
||||
this.options = {
|
||||
legend: {
|
||||
data: ['bar', 'bar2'],
|
||||
align: 'left',
|
||||
},
|
||||
tooltip: {},
|
||||
xAxis: {
|
||||
data: xAxisData,
|
||||
silent: false,
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yAxis: {},
|
||||
series: [
|
||||
{
|
||||
name: 'bar',
|
||||
type: 'bar',
|
||||
data: data1,
|
||||
animationDelay: (idx: number) => idx * 10,
|
||||
},
|
||||
{
|
||||
name: 'bar2',
|
||||
type: 'bar',
|
||||
data: data2,
|
||||
animationDelay: (idx: number) => idx * 10 + 100,
|
||||
},
|
||||
],
|
||||
animationEasing: 'elasticOut',
|
||||
animationDelayUpdate: (idx: number) => idx * 5,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {Monitor} from "../../../pojo/Monitor";
|
||||
import {FormGroup} from "@angular/forms";
|
||||
import {Message} from "../../../pojo/Message";
|
||||
import {throwError} from "rxjs";
|
||||
import {TitleService} from "@delon/theme";
|
||||
|
||||
@Component({
|
||||
selector: 'app-monitor-modify',
|
||||
@@ -23,6 +24,7 @@ export class MonitorEditComponent implements OnInit {
|
||||
private monitorSvc: MonitorService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private titleSvc: TitleService,
|
||||
private notifySvc: NzNotificationService,) { }
|
||||
|
||||
paramDefines!: ParamDefine[];
|
||||
@@ -47,6 +49,7 @@ export class MonitorEditComponent implements OnInit {
|
||||
).pipe(switchMap((message: Message<any>) => {
|
||||
if (message.code === 0) {
|
||||
this.monitor = message.data.monitor;
|
||||
this.titleSvc.setTitleByI18n('monitor.app.' + this.monitor.app)
|
||||
if (message.data.params != null) {
|
||||
message.data.params.forEach((item: Param) => {
|
||||
this.paramValueMap.set(item.field, item)
|
||||
|
||||
@@ -58,7 +58,11 @@
|
||||
<tbody>
|
||||
<tr *ngFor="let data of fixedTable.data">
|
||||
<td nzAlign="center" nzLeft [nzChecked]="checkedMonitorIds.has(data.id)" (nzCheckedChange)="onItemChecked(data.id, $event)"></td>
|
||||
<td nzAlign="center">{{ data.name }}</td>
|
||||
<td nzAlign="center">
|
||||
<a [routerLink]="['/monitors/' + data.id]">
|
||||
<span>{{ data.name }}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td nzAlign="center">
|
||||
<nz-tag *ngIf="data.status == 0" nzColor="default">
|
||||
<i nz-icon nzType="robot" nzTheme="outline"></i>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {Param} from "../../../pojo/Param";
|
||||
import {Monitor} from "../../../pojo/Monitor";
|
||||
import {MonitorService} from "../../../service/monitor.service";
|
||||
import {NzNotificationService} from "ng-zorro-antd/notification";
|
||||
import {TitleService} from "@delon/theme";
|
||||
|
||||
@Component({
|
||||
selector: 'app-monitor-add',
|
||||
@@ -32,6 +33,7 @@ export class MonitorNewComponent implements OnInit {
|
||||
private notifySvc: NzNotificationService,
|
||||
private cdr: ChangeDetectorRef,
|
||||
private i18n: I18NService,
|
||||
private titleSvc: TitleService,
|
||||
private formBuilder: FormBuilder) {
|
||||
this.monitor = new Monitor();
|
||||
}
|
||||
@@ -40,6 +42,7 @@ export class MonitorNewComponent implements OnInit {
|
||||
this.route.queryParamMap.pipe(
|
||||
switchMap((paramMap: ParamMap) => {
|
||||
this.monitor.app = paramMap.get("app") || '';
|
||||
this.titleSvc.setTitleByI18n('monitor.app.' + this.monitor.app)
|
||||
this.detected = true;
|
||||
this.passwordVisible = false;
|
||||
this.isSpinning = false;
|
||||
|
||||
@@ -10,6 +10,7 @@ import {NzDividerModule} from "ng-zorro-antd/divider";
|
||||
import {NzSwitchModule} from "ng-zorro-antd/switch";
|
||||
import {NzTagModule} from "ng-zorro-antd/tag";
|
||||
import {NzRadioModule} from "ng-zorro-antd/radio";
|
||||
import {NgxEchartsModule} from "ngx-echarts";
|
||||
|
||||
const COMPONENTS: Type<void>[] = [
|
||||
MonitorNewComponent,
|
||||
@@ -26,7 +27,8 @@ const COMPONENTS: Type<void>[] = [
|
||||
NzDividerModule,
|
||||
NzSwitchModule,
|
||||
NzTagModule,
|
||||
NzRadioModule
|
||||
NzRadioModule,
|
||||
NgxEchartsModule
|
||||
],
|
||||
declarations: COMPONENTS,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user