新增监控日志

This commit is contained in:
tanghc
2019-06-06 15:44:47 +08:00
parent f2692c509e
commit 12fc71dd57
47 changed files with 1056 additions and 96 deletions

View File

@@ -9,39 +9,27 @@ import com.gitee.easyopen.util.CopyUtil;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.fastmybatis.core.query.Sort;
import com.gitee.fastmybatis.core.query.expression.ValueExpression;
import com.gitee.fastmybatis.core.util.MapperUtil;
import com.gitee.fastmybatis.core.util.MyBeanUtil;
import com.gitee.sop.adminserver.api.service.param.LimitNewAddParam;
import com.gitee.sop.adminserver.api.service.param.LimitNewParam;
import com.gitee.sop.adminserver.api.service.param.LimitNewUpdateParam;
import com.gitee.sop.adminserver.api.service.param.RouteSearchParam;
import com.gitee.sop.adminserver.api.service.result.LimitNewVO;
import com.gitee.sop.adminserver.bean.ConfigLimitDto;
import com.gitee.sop.adminserver.bean.GatewayRouteDefinition;
import com.gitee.sop.adminserver.common.LimitEnum;
import com.gitee.sop.adminserver.entity.ConfigLimit;
import com.gitee.sop.adminserver.mapper.ConfigLimitMapper;
import com.gitee.sop.adminserver.service.RouteConfigService;
import com.gitee.sop.adminserver.service.RouteService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 限流
*
* @author tanghc
*/
@ApiService
@ApiDoc("服务管理")
@ApiDoc("服务管理-限流管理")
@Slf4j
public class LimitNewApi {

View File

@@ -0,0 +1,169 @@
package com.gitee.sop.adminserver.api.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.exception.ApiException;
import com.gitee.easyopen.verify.DefaultMd5Verifier;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.adminserver.api.service.param.LogMonitorInstanceAddParam;
import com.gitee.sop.adminserver.api.service.result.LogMonitorInstanceVO;
import com.gitee.sop.adminserver.common.QueryUtil;
import com.gitee.sop.adminserver.entity.ConfigCommon;
import com.gitee.sop.adminserver.mapper.ConfigCommonMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author tanghc
*/
@ApiService
@ApiDoc("服务管理-日志监控")
@Slf4j
public class LogApi {
public static final String LOG_MONITOR_INSTANCE = "log.monitor.instance";
public static final String CODE_SUCCESS = "10000";
@Autowired
ConfigCommonMapper configCommonMapper;
RestTemplate restTemplate = new RestTemplate();
@Value("${zuul.secret}")
private String secret;
@Api(name = "monitor.log.list")
@ApiDocMethod(description = "获取监控日志")
List<LogMonitorInstanceVO> listLog() {
List<ConfigCommon> configCommonList = configCommonMapper.listByColumn("config_group", LOG_MONITOR_INSTANCE);
List<LogMonitorInstanceVO> ret = new ArrayList<>();
int id = 1;
for (ConfigCommon configCommon : configCommonList) {
int pid = id++;
String ipPort = configCommon.getConfigKey();
// 父节点
LogMonitorInstanceVO logMonitorInstanceVOParent = new LogMonitorInstanceVO();
logMonitorInstanceVOParent.setRawId(configCommon.getId());
logMonitorInstanceVOParent.setTreeId(pid);
logMonitorInstanceVOParent.setMonitorName(configCommon.getContent());
ret.add(logMonitorInstanceVOParent);
try {
String logData = this.requestLogServer(ipPort, "listErrors");
JSONObject jsonObject = JSON.parseObject(logData);
if (CODE_SUCCESS.equals(jsonObject.getString("code"))) {
int errorTotal = 0;
List<LogMonitorInstanceVO> data = JSON.parseArray(jsonObject.getString("data"), LogMonitorInstanceVO.class);
for (LogMonitorInstanceVO instanceVO : data) {
instanceVO.setTreeId(id++);
instanceVO.setParentId(pid);
errorTotal += instanceVO.getCount();
}
ret.addAll(data);
logMonitorInstanceVOParent.setCount(errorTotal);
}
} catch (Exception e) {
log.error("获取日志信息出错", e);
throw new ApiException("获取日志信息出错");
}
}
Collections.sort(ret, Comparator.comparing(LogMonitorInstanceVO::getCount));
return ret;
}
@Api(name = "monitor.log.clear")
@ApiDocMethod(description = "清空日志")
void clearLog(@NotNull(message = "id不能为空") Long id) {
ConfigCommon configCommon = configCommonMapper.getById(id);
if (configCommon == null) {
return;
}
try {
String ipPort = configCommon.getConfigKey();
this.requestLogServer(ipPort, "clearErrors");
} catch (Exception e) {
throw new ApiException("清除失败");
}
}
@Api(name = "monitor.instance.list")
@ApiDocMethod(description = "获取已添加的监控实例")
List<String> listServiceInstance() {
List<ConfigCommon> configCommonList = configCommonMapper.listByColumn("config_group", LOG_MONITOR_INSTANCE);
return configCommonList.stream()
.map(ConfigCommon::getConfigKey)
.collect(Collectors.toList());
}
@Api(name = "monitor.instance.add")
@ApiDocMethod(description = "添加监控实例")
void addServiceInstance(LogMonitorInstanceAddParam param) {
String ipPort = param.getIp() + ":" + param.getPort();
this.checkInstance(ipPort);
Query query = new Query();
query.eq("config_group", LOG_MONITOR_INSTANCE)
.eq("config_key", ipPort);
ConfigCommon rec = configCommonMapper.getByQuery(query);
if (rec != null) {
throw new ApiException("该实例已添加");
}
ConfigCommon configCommon = new ConfigCommon();
configCommon.setConfigGroup(LOG_MONITOR_INSTANCE);
configCommon.setConfigKey(ipPort);
configCommon.setContent(param.getServiceId() + "(" + ipPort + ")");
configCommonMapper.saveIgnoreNull(configCommon);
}
private void checkInstance(String ipPort) {
try {
String json = this.requestLogServer(ipPort, "listErrors");
JSONObject jsonObject = JSON.parseObject(json);
if (!CODE_SUCCESS.equals(jsonObject.getString("code"))) {
log.error("请求结果:{}", json);
throw new ApiException("添加失败");
}
} catch (Exception e) {
log.error("添加失败", e);
throw new ApiException("添加失败");
}
}
private String requestLogServer(String ipPort, String path) throws Exception {
DefaultMd5Verifier md5Verifier = new DefaultMd5Verifier();
Map<String, Object> params = new HashMap<>();
params.put("time", System.currentTimeMillis());
String sign = md5Verifier.buildSign(params, secret);
params.put("sign", sign);
String query = QueryUtil.buildQueryString(params);
String url = "http://" + ipPort + "/" + path + "?" + query;
ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class);
if (entity.getStatusCode() != HttpStatus.OK) {
throw new IllegalAccessException("无权访问");
}
return entity.getBody();
}
@Api(name = "monitor.instance.del")
@ApiDocMethod(description = "删除监控实例")
void delServiceInstance(@NotNull(message = "id不能为空") Long id) {
configCommonMapper.deleteById(id);
}
}

View File

@@ -43,7 +43,7 @@ import java.util.stream.Collectors;
* @author tanghc
*/
@ApiService
@ApiDoc("服务管理")
@ApiDoc("服务管理-路由管理")
@Slf4j
public class RouteApi {

View File

@@ -36,7 +36,7 @@ import java.util.stream.Collectors;
* @author tanghc
*/
@ApiService
@ApiDoc("服务管理")
@ApiDoc("服务管理-服务列表")
@Slf4j
public class ServiceApi {

View File

@@ -0,0 +1,19 @@
package com.gitee.sop.adminserver.api.service.param;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author tanghc
*/
@Data
public class LogMonitorInstanceAddParam {
@NotBlank(message = "serviceId不能为空")
private String serviceId;
@NotBlank(message = "ip不能为空")
private String ip;
private int port;
}

View File

@@ -0,0 +1,22 @@
package com.gitee.sop.adminserver.api.service.result;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class LogMonitorInstanceVO {
private String id;
private int treeId;
// 表主键
private long rawId;
private String name;
private String version;
private String serviceId;
private String errorMsg;
private long count;
private int parentId;
private String monitorName;
}

View File

@@ -0,0 +1,28 @@
package com.gitee.sop.adminserver.common;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
/**
* @author tanghc
*/
public class QueryUtil {
public static String buildQueryString(Map<String, ?> params) throws UnsupportedEncodingException {
if (params == null || params.size() == 0) {
return "";
}
StringBuilder query = new StringBuilder();
int i = 0;
for (Map.Entry<String, ?> entry : params.entrySet()) {
String name = entry.getKey();
String value = String.valueOf(entry.getValue());
if (i++ > 0) {
query.append("&");
}
query.append(name).append("=").append(URLEncoder.encode(value, "UTF-8"));
}
return query.toString();
}
}

View File

@@ -32,9 +32,10 @@ public class WebConfig {
@Bean
ApiConfig apiConfig() {
ApiConfig apiConfig = new ApiConfig();
apiConfig.setJsonResultSerializer(obj -> {
return JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat);
});
apiConfig.setJsonResultSerializer(obj -> JSON.toJSONString(obj,
SerializerFeature.WriteDateUseDateFormat
, SerializerFeature.WriteNullStringAsEmpty
, SerializerFeature.WriteMapNullValue));
ApiSessionManager apiSessionManager = new ApiSessionManager();
// session有效期
int timeout = NumberUtils.toInt(accessTokenTimeout, 30);

View File

@@ -0,0 +1,45 @@
package com.gitee.sop.adminserver.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
/**
* 表名config_common
* 备注:通用配置表
*
* @author tanghc
*/
@Table(name = "config_common")
@Data
public class ConfigCommon {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
/** 数据库字段id */
private Long id;
/** 配置分组, 数据库字段config_group */
private String configGroup;
/** 配置key, 数据库字段config_key */
private String configKey;
/** 内容, 数据库字段content */
private String content;
/** 备注, 数据库字段remark */
private String remark;
/** 数据库字段gmt_create */
private Date gmtCreate;
/** 数据库字段gmt_modified */
private Date gmtModified;
}

View File

@@ -0,0 +1,12 @@
package com.gitee.sop.adminserver.mapper;
import com.gitee.fastmybatis.core.mapper.CrudMapper;
import com.gitee.sop.adminserver.entity.ConfigCommon;
/**
* @author tanghc
*/
public interface ConfigCommonMapper extends CrudMapper<ConfigCommon, Long> {
}

View File

@@ -48,3 +48,7 @@ logging:
mybatis:
fill: {com.gitee.fastmybatis.core.support.DateFillInsert: gmt_create,
com.gitee.fastmybatis.core.support.DateFillUpdate: gmt_modified}
# 不用改,如果要改,建议全局替换修改
zuul:
secret: MZZOUSTua6LzApIWXCwEgbBmxSzpzC

View File

@@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.18b11d0e.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.4f0872ef.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function t(t){for(var r,o,c=t[0],i=t[1],f=t[2],l=0,s=[];l<c.length;l++)o=c[l],u[o]&&s.push(u[o][0]),u[o]=0;for(r in i)Object.prototype.hasOwnProperty.call(i,r)&&(e[r]=i[r]);d&&d(t);while(s.length)s.shift()();return a.push.apply(a,f||[]),n()}function n(){for(var e,t=0;t<a.length;t++){for(var n=a[t],r=!0,o=1;o<n.length;o++){var c=n[o];0!==u[c]&&(r=!1)}r&&(a.splice(t--,1),e=i(i.s=n[0]))}return e}var r={},o={runtime:0},u={runtime:0},a=[];function c(e){return i.p+"static/js/"+({}[e]||e)+"."+{"chunk-19100986":"3ec98327","chunk-238a81e9":"5955f13d","chunk-29b24c82":"2b3ac7ca","chunk-2d0aa95b":"5ba26eff","chunk-2d221836":"574c9ab6","chunk-2d22c2e3":"0cbb7297","chunk-ea2e58a4":"f3f85b0e"}[e]+".js"}function i(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.e=function(e){var t=[],n={"chunk-19100986":1,"chunk-238a81e9":1,"chunk-29b24c82":1,"chunk-ea2e58a4":1};o[e]?t.push(o[e]):0!==o[e]&&n[e]&&t.push(o[e]=new Promise(function(t,n){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-19100986":"a43114f3","chunk-238a81e9":"e8e2beee","chunk-29b24c82":"857979fb","chunk-2d0aa95b":"31d6cfe0","chunk-2d221836":"31d6cfe0","chunk-2d22c2e3":"31d6cfe0","chunk-ea2e58a4":"d10599db"}[e]+".css",u=i.p+r,a=document.getElementsByTagName("link"),c=0;c<a.length;c++){var f=a[c],l=f.getAttribute("data-href")||f.getAttribute("href");if("stylesheet"===f.rel&&(l===r||l===u))return t()}var s=document.getElementsByTagName("style");for(c=0;c<s.length;c++){f=s[c],l=f.getAttribute("data-href");if(l===r||l===u)return t()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=t,d.onerror=function(t){var r=t&&t.target&&t.target.src||u,a=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");a.code="CSS_CHUNK_LOAD_FAILED",a.request=r,delete o[e],d.parentNode.removeChild(d),n(a)},d.href=u;var h=document.getElementsByTagName("head")[0];h.appendChild(d)}).then(function(){o[e]=0}));var r=u[e];if(0!==r)if(r)t.push(r[2]);else{var a=new Promise(function(t,n){r=u[e]=[t,n]});t.push(r[2]=a);var f,l=document.createElement("script");l.charset="utf-8",l.timeout=120,i.nc&&l.setAttribute("nonce",i.nc),l.src=c(e),f=function(t){l.onerror=l.onload=null,clearTimeout(s);var n=u[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src,a=new Error("Loading chunk "+e+" failed.\n("+r+": "+o+")");a.type=r,a.request=o,n[1](a)}u[e]=void 0}};var s=setTimeout(function(){f({type:"timeout",target:l})},12e4);l.onerror=l.onload=f,document.head.appendChild(l)}return Promise.all(t)},i.m=e,i.c=r,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i.oe=function(e){throw console.error(e),e};var f=window["webpackJsonp"]=window["webpackJsonp"]||[],l=f.push.bind(f);f.push=t,f=f.slice();for(var s=0;s<f.length;s++)t(f[s]);var d=l;n()})([]);</script><script src=static/js/chunk-elementUI.fa810e14.js></script><script src=static/js/chunk-libs.a463a5ed.js></script><script src=static/js/app.bc31495f.js></script></body></html>
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.18b11d0e.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.4f0872ef.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function t(t){for(var r,o,a=t[0],i=t[1],f=t[2],l=0,d=[];l<a.length;l++)o=a[l],u[o]&&d.push(u[o][0]),u[o]=0;for(r in i)Object.prototype.hasOwnProperty.call(i,r)&&(e[r]=i[r]);s&&s(t);while(d.length)d.shift()();return c.push.apply(c,f||[]),n()}function n(){for(var e,t=0;t<c.length;t++){for(var n=c[t],r=!0,o=1;o<n.length;o++){var a=n[o];0!==u[a]&&(r=!1)}r&&(c.splice(t--,1),e=i(i.s=n[0]))}return e}var r={},o={runtime:0},u={runtime:0},c=[];function a(e){return i.p+"static/js/"+({}[e]||e)+"."+{"chunk-19100986":"3ec98327","chunk-238a81e9":"5955f13d","chunk-29b24c82":"2b3ac7ca","chunk-2d0aa95b":"447435b3","chunk-2d221836":"574c9ab6","chunk-2d22c2e3":"46f37153","chunk-4a59cbe4":"3309de97","chunk-ea2e58a4":"f3f85b0e"}[e]+".js"}function i(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.e=function(e){var t=[],n={"chunk-19100986":1,"chunk-238a81e9":1,"chunk-29b24c82":1,"chunk-ea2e58a4":1};o[e]?t.push(o[e]):0!==o[e]&&n[e]&&t.push(o[e]=new Promise(function(t,n){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-19100986":"a43114f3","chunk-238a81e9":"e8e2beee","chunk-29b24c82":"857979fb","chunk-2d0aa95b":"31d6cfe0","chunk-2d221836":"31d6cfe0","chunk-2d22c2e3":"31d6cfe0","chunk-4a59cbe4":"31d6cfe0","chunk-ea2e58a4":"d10599db"}[e]+".css",u=i.p+r,c=document.getElementsByTagName("link"),a=0;a<c.length;a++){var f=c[a],l=f.getAttribute("data-href")||f.getAttribute("href");if("stylesheet"===f.rel&&(l===r||l===u))return t()}var d=document.getElementsByTagName("style");for(a=0;a<d.length;a++){f=d[a],l=f.getAttribute("data-href");if(l===r||l===u)return t()}var s=document.createElement("link");s.rel="stylesheet",s.type="text/css",s.onload=t,s.onerror=function(t){var r=t&&t.target&&t.target.src||u,c=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");c.code="CSS_CHUNK_LOAD_FAILED",c.request=r,delete o[e],s.parentNode.removeChild(s),n(c)},s.href=u;var h=document.getElementsByTagName("head")[0];h.appendChild(s)}).then(function(){o[e]=0}));var r=u[e];if(0!==r)if(r)t.push(r[2]);else{var c=new Promise(function(t,n){r=u[e]=[t,n]});t.push(r[2]=c);var f,l=document.createElement("script");l.charset="utf-8",l.timeout=120,i.nc&&l.setAttribute("nonce",i.nc),l.src=a(e),f=function(t){l.onerror=l.onload=null,clearTimeout(d);var n=u[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src,c=new Error("Loading chunk "+e+" failed.\n("+r+": "+o+")");c.type=r,c.request=o,n[1](c)}u[e]=void 0}};var d=setTimeout(function(){f({type:"timeout",target:l})},12e4);l.onerror=l.onload=f,document.head.appendChild(l)}return Promise.all(t)},i.m=e,i.c=r,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i.oe=function(e){throw console.error(e),e};var f=window["webpackJsonp"]=window["webpackJsonp"]||[],l=f.push.bind(f);f.push=t,f=f.slice();for(var d=0;d<f.length;d++)t(f[d]);var s=l;n()})([]);</script><script src=static/js/chunk-elementUI.fa810e14.js></script><script src=static/js/chunk-libs.a463a5ed.js></script><script src=static/js/app.36a3cb86.js></script></body></html>

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d22c2e3"],{f1ac:function(t,e,a){"use strict";a.r(e);var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"app-container"},[a("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:t.searchFormData,size:"mini"}},[a("el-form-item",{attrs:{label:"serviceId"}},[a("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"serviceId"},model:{value:t.searchFormData.serviceId,callback:function(e){t.$set(t.searchFormData,"serviceId",e)},expression:"searchFormData.serviceId"}})],1),t._v(" "),a("el-form-item",[a("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.onSearchTable}},[t._v("查询")])],1)],1),t._v(" "),a("el-table",{staticStyle:{width:"100%","margin-bottom":"20px"},attrs:{data:t.tableData,border:"","row-key":"id"}},[a("el-table-column",{attrs:{prop:"name",label:"服务名称(serviceId)",width:"200"}}),t._v(" "),a("el-table-column",{attrs:{prop:"instanceId",label:"instanceId",width:"250"}}),t._v(" "),a("el-table-column",{attrs:{prop:"ipAddr",label:"IP地址",width:"150"}}),t._v(" "),a("el-table-column",{attrs:{prop:"serverPort",label:"端口号",width:"100"}}),t._v(" "),a("el-table-column",{attrs:{prop:"status",label:"服务状态",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?a("el-tag",{attrs:{type:"success"}},[t._v("已上线")]):t._e(),t._v(" "),e.row.parentId>0&&"STARTING"===e.row.status?a("el-tag",{attrs:{type:"info"}},[t._v("正在启动")]):t._e(),t._v(" "),e.row.parentId>0&&"UNKNOWN"===e.row.status?a("el-tag",[t._v("未知")]):t._e(),t._v(" "),e.row.parentId>0&&("OUT_OF_SERVICE"===e.row.status||"DOWN"===e.row.status)?a("el-tag",{attrs:{type:"danger"}},[t._v("已下线")]):t._e()]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"updateTime",label:"最后更新时间",width:"160"}}),t._v(" "),a("el-table-column",{attrs:{label:"操作",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return t.onOffline(e.row)}}},[t._v("下线")]):t._e(),t._v(" "),e.row.parentId>0&&"OUT_OF_SERVICE"===e.row.status?a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return t.onOnline(e.row)}}},[t._v("上线")]):t._e()]}}])})],1)],1)},r=[],i=(a("7f7f"),a("ac6a"),{data:function(){return{searchFormData:{serviceId:""},tableData:[]}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("service.instance.list",this.searchFormData,function(t){this.tableData=this.buildTreeData(t.data)})},buildTreeData:function(t){return t.forEach(function(e){var a=e.parentId;0===a||t.forEach(function(t){if(t.id===a){var n=t.child;n||(n=[]),n.push(e),t.children=n}})}),t=t.filter(function(t){return 0===t.parentId}),t},onSearchTable:function(){this.loadTable()},onOffline:function(t){this.confirm("确定要下线【"+t.name+"】吗?",function(e){var a={serviceId:t.name,instanceId:t.instanceId};this.post("service.instance.offline",a,function(){this.tip("下线成功"),e()})})},onOnline:function(t){this.confirm("确定要上线【"+t.name+"】吗?",function(e){var a={serviceId:t.name,instanceId:t.instanceId};this.post("service.instance.online",a,function(){this.tip("上线成功"),e()})})}}}),s=i,o=a("2877"),l=Object(o["a"])(s,n,r,!1,null,null,null);e["default"]=l.exports}}]);
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d22c2e3"],{f1ac:function(t,e,a){"use strict";a.r(e);var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"app-container"},[a("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:t.searchFormData,size:"mini"}},[a("el-form-item",{attrs:{label:"serviceId"}},[a("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"serviceId"},model:{value:t.searchFormData.serviceId,callback:function(e){t.$set(t.searchFormData,"serviceId",e)},expression:"searchFormData.serviceId"}})],1),t._v(" "),a("el-form-item",[a("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.onSearchTable}},[t._v("查询")])],1)],1),t._v(" "),a("el-table",{staticStyle:{width:"100%","margin-bottom":"20px"},attrs:{data:t.tableData,border:"","row-key":"id"}},[a("el-table-column",{attrs:{prop:"name",label:"服务名称(serviceId)",width:"200"}}),t._v(" "),a("el-table-column",{attrs:{prop:"instanceId",label:"instanceId",width:"250"}}),t._v(" "),a("el-table-column",{attrs:{prop:"ipAddr",label:"IP地址",width:"150"}}),t._v(" "),a("el-table-column",{attrs:{prop:"serverPort",label:"端口号",width:"100"}}),t._v(" "),a("el-table-column",{attrs:{prop:"status",label:"服务状态",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?a("el-tag",{attrs:{type:"success"}},[t._v("已上线")]):t._e(),t._v(" "),e.row.parentId>0&&"STARTING"===e.row.status?a("el-tag",{attrs:{type:"info"}},[t._v("正在启动")]):t._e(),t._v(" "),e.row.parentId>0&&"UNKNOWN"===e.row.status?a("el-tag",[t._v("未知")]):t._e(),t._v(" "),e.row.parentId>0&&("OUT_OF_SERVICE"===e.row.status||"DOWN"===e.row.status)?a("el-tag",{attrs:{type:"danger"}},[t._v("已下线")]):t._e()]}}])}),t._v(" "),a("el-table-column",{attrs:{prop:"updateTime",label:"最后更新时间",width:"160"}}),t._v(" "),a("el-table-column",{attrs:{label:"操作",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return t.onOffline(e.row)}}},[t._v("下线")]):t._e(),t._v(" "),e.row.parentId>0&&"OUT_OF_SERVICE"===e.row.status?a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return t.onOnline(e.row)}}},[t._v("上线")]):t._e()]}}])})],1)],1)},r=[],i=(a("7f7f"),a("ac6a"),{data:function(){return{searchFormData:{serviceId:""},tableData:[]}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("service.instance.list",this.searchFormData,function(t){this.tableData=this.buildTreeData(t.data)})},buildTreeData:function(t){return t.forEach(function(e){var a=e.parentId;0===a||t.forEach(function(t){if(t.id===a){var n=t.children;n||(n=[]),n.push(e),t.children=n}})}),t=t.filter(function(t){return 0===t.parentId}),t},onSearchTable:function(){this.loadTable()},onOffline:function(t){this.confirm("确定要下线【"+t.name+"】吗?",function(e){var a={serviceId:t.name,instanceId:t.instanceId};this.post("service.instance.offline",a,function(){this.tip("下线成功"),e()})})},onOnline:function(t){this.confirm("确定要上线【"+t.name+"】吗?",function(e){var a={serviceId:t.name,instanceId:t.instanceId};this.post("service.instance.online",a,function(){this.tip("上线成功"),e()})})}}}),s=i,o=a("2877"),l=Object(o["a"])(s,n,r,!1,null,null,null);e["default"]=l.exports}}]);

File diff suppressed because one or more lines are too long

View File

@@ -78,6 +78,12 @@ export const constantRoutes = [
name: 'Limit',
component: () => import('@/views/service/limit/index2'),
meta: { title: '限流管理' }
},
{
path: 'log',
name: 'Log',
component: () => import('@/views/service/log/index'),
meta: { title: '监控日志' }
}
]
},

View File

@@ -35,7 +35,7 @@ Object.assign(Vue.prototype, {
const resp = response.data
const code = resp.code
if (!code || code === '-9') {
that.$message.error('系统错误')
that.$message.error(resp.msg || '系统错误')
return
}
if (code === '-100' || code === '18' || code === '21') { // 未登录
@@ -96,6 +96,9 @@ Object.assign(Vue.prototype, {
},
logout: function() {
removeToken()
this.$router.push({ path: `/login?redirect=${this.$route.fullPath}` })
const fullPath = this.$route.fullPath
if (fullPath.indexOf('login?redirect') === -1) {
this.$router.push({ path: `/login?redirect=${fullPath}` })
}
}
})

View File

@@ -92,7 +92,7 @@ export default {
// 如果ele是子元素的话 ,把ele扔到他的父亲的child数组中.
data.forEach(d => {
if (d.id === parentId) {
let childArray = d.child
let childArray = d.children
if (!childArray) {
childArray = []
}

View File

@@ -0,0 +1,234 @@
<template>
<div class="app-container">
<el-form :inline="true" :model="searchFormData" class="demo-form-inline" size="mini">
<el-form-item>
<el-button type="primary" icon="el-icon-plus" @click="onAddServer">添加监控服务器</el-button>
</el-form-item>
</el-form>
<el-table
:data="tableData"
style="width: 100%;margin-bottom: 20px;"
border
row-key="treeId"
empty-text="请添加监控服务器"
>
<el-table-column
prop="monitorName"
label="网关实例"
width="300"
>
<template slot-scope="scope">
<span v-if="scope.row.parentId === 0">{{ scope.row.monitorName }}</span>
</template>
</el-table-column>
<el-table-column
prop="serviceId"
label="serviceId"
width="200"
>
<template slot-scope="scope">
<span v-if="scope.row.parentId > 0">{{ scope.row.serviceId }}</span>
</template>
</el-table-column>
<el-table-column
prop="name"
label="接口名 (版本号)"
width="200"
>
<template slot-scope="scope">
{{ scope.row.name + (scope.row.version ? ' (' + scope.row.version + ')' : '') }}
</template>
</el-table-column>
<el-table-column
prop="count"
label="出错次数"
width="100"
/>
<el-table-column
prop="errorMsg"
label="报错信息"
width="300"
>
<template v-if="scope.row.parentId > 0" slot-scope="scope">
<div style="display: inline-block;" v-html="showErrorMsg(scope.row)"></div> <el-button type="text" size="mini" @click="onShowErrorDetail(scope.row)">详情</el-button>
</template>
</el-table-column>
<el-table-column
label="操作"
width="180"
>
<template slot-scope="scope">
<el-button v-if="scope.row.parentId === 0 && scope.row.children" type="text" size="mini" @click="onClearLog(scope.row)">清空日志</el-button>
<el-button v-if="scope.row.parentId === 0" type="text" size="mini" @click="onDelete(scope.row)">删除实例</el-button>
</template>
</el-table-column>
</el-table>
<!-- dialog -->
<el-dialog
title="选择服务器实例"
:visible.sync="logDialogInstanceVisible"
:close-on-click-modal="false"
>
<el-form
ref="logDialogForm"
:model="logDialogFormData"
:rules="rulesLog"
label-width="150px"
size="mini"
>
<el-form-item>
<p style="color: #878787;">只能选择网关实例其它实例不支持</p>
</el-form-item>
<el-form-item prop="instanceData" label="服务器实例">
<el-select v-model="logDialogFormData.instanceData" value-key="id" style="width: 400px;">
<el-option
v-for="item in serviceData"
:key="item.id"
:label="item.name + '(' + item.ipAddr + ':' + item.serverPort + ')'"
:value="item"
:disabled="isOptionDisabled(item)"
>
<span style="float: left">{{ item.name }} <span v-if="isOptionDisabled(item)">(已添加)</span></span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.ipAddr + ':' + item.serverPort }}</span>
</el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="logDialogInstanceVisible = false"> </el-button>
<el-button type="primary" @click="onLogDialogSave"> </el-button>
</div>
</el-dialog>
<el-dialog
title="错误详情"
:visible.sync="logDetailVisible"
width="60%"
>
<div style="overflow-x: auto" v-html="errorMsgDetail"></div>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="logDetailVisible = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
searchFormData: {},
tableData: [],
serviceData: [],
// 已经添加的实例
addedInstanceList: [],
logDialogFormData: {
instanceData: null
},
logDialogInstanceVisible: false,
logDetailVisible: false,
rulesLog: {
instanceData: [
{ required: true, message: '不能为空', trigger: 'blur' }
]
},
errorMsgDetail: ''
}
},
created() {
this.loadServiceInstance()
this.loadTable()
},
methods: {
loadServiceInstance: function() {
this.post('service.instance.list', {}, function(resp) {
this.serviceData = resp.data.filter(el => {
return el.instanceId && el.instanceId.length > 0
})
})
this.post('monitor.instance.list', {}, function(resp) {
this.addedInstanceList = resp.data
})
},
loadTable: function() {
this.post('monitor.log.list', {}, function(resp) {
this.tableData = this.buildTreeData(resp.data)
})
},
isOptionDisabled: function(item) {
const ipPort = item.ipAddr + ':' + item.serverPort
const index = this.addedInstanceList.findIndex((value, index, arr) => {
return value === ipPort
})
return index > -1
},
buildTreeData: function(data) {
data.forEach(ele => {
const parentId = ele.parentId
if (parentId === 0) {
// 是根元素 ,不做任何操作,如果是正常的for-i循环,可以直接continue.
} else {
// 如果ele是子元素的话 ,把ele扔到他的父亲的child数组中.
data.forEach(d => {
if (d.treeId === parentId) {
let childArray = d.children
if (!childArray) {
childArray = []
}
childArray.push(ele)
d.children = childArray
}
})
}
})
// 去除重复元素
data = data.filter(ele => ele.parentId === 0)
return data
},
showErrorMsg: function(row) {
const msg = row.errorMsg.replace(/\<br\>/g, '')
return msg.substring(0, 30) + '...'
},
onAddServer: function() {
this.logDialogInstanceVisible = true
},
onDelete: function(row) {
this.confirm('确定要删除实例【' + row.monitorName + '】吗?', function(done) {
this.post('monitor.instance.del', { id: row.rawId }, function(resp) {
done()
this.tip('删除成功')
this.loadTable()
})
})
},
onClearLog: function(row) {
this.confirm('确定要清空日志吗?', function(done) {
this.post('monitor.log.clear', { id: row.rawId }, function(resp) {
done()
this.tip('清空成功')
this.loadTable()
})
})
},
onShowErrorDetail: function(row) {
this.errorMsgDetail = row.errorMsg
this.logDetailVisible = true
},
onLogDialogSave: function() {
this.$refs['logDialogForm'].validate((valid) => {
if (valid) {
const instanceData = this.logDialogFormData.instanceData
const data = {
serviceId: instanceData.serviceId,
ip: instanceData.ipAddr,
port: instanceData.serverPort
}
this.post('monitor.instance.add', data, function(resp) {
this.logDialogInstanceVisible = false
this.loadTable()
})
}
})
}
}
}
</script>

View File

@@ -79,12 +79,12 @@
</el-table-column>
<el-table-column
prop="mergeResult"
label="合并结果"
width="80"
label="统一格式输出"
width="120"
>
<template slot-scope="scope">
<span v-if="scope.row.mergeResult === 1" style="color:#67C23A">合并</span>
<span v-if="scope.row.mergeResult === 0" style="color:#E6A23C">不合并</span>
<span v-if="scope.row.mergeResult === 1" style="color:#67C23A"></span>
<span v-if="scope.row.mergeResult === 0" style="color:#E6A23C"></span>
</template>
</el-table-column>
<el-table-column