Commit 32b2b5dd authored by nobodyiam's avatar nobodyiam

add system info page for admin user to diagnose the system

parent 23f823a3
......@@ -53,6 +53,9 @@ public class MetaDomainConsts {
private static final Object LOCK = new Object();
/**
* Return one meta server address. If multiple meta server addresses are configured, will select one.
*/
public static String getDomain(Env env) {
String metaServerAddress = getMetaServerAddress(env);
// if there is more than one address, need to select one
......@@ -62,7 +65,10 @@ public class MetaDomainConsts {
return metaServerAddress;
}
private static String getMetaServerAddress(Env env) {
/**
* Return meta server address. If multiple meta server addresses are configured, will return the comma separated string.
*/
public static String getMetaServerAddress(Env env) {
if (!metaServerAddressCache.containsKey(env)) {
initMetaServerAddress(env);
}
......
package com.ctrip.framework.apollo.portal.controller;
import com.ctrip.framework.apollo.Apollo;
import com.ctrip.framework.apollo.core.MetaDomainConsts;
import com.ctrip.framework.apollo.core.dto.ServiceDTO;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.component.PortalSettings;
import com.ctrip.framework.apollo.portal.component.RestTemplateFactory;
import com.ctrip.framework.apollo.portal.entity.vo.EnvironmentInfo;
import com.ctrip.framework.apollo.portal.entity.vo.SystemInfo;
import java.util.List;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/system-info")
public class SystemInfoController {
private static final Logger logger = LoggerFactory.getLogger(SystemInfoController.class);
private static final String CONFIG_SERVICE_URL_PATH = "/services/config";
private static final String ADMIN_SERVICE_URL_PATH = "/services/admin";
private RestTemplate restTemplate;
@Autowired
private PortalSettings portalSettings;
@Autowired
private RestTemplateFactory restTemplateFactory;
@PostConstruct
private void init() {
restTemplate = restTemplateFactory.getObject();
}
@PreAuthorize(value = "@permissionValidator.isSuperAdmin()")
@RequestMapping(method = RequestMethod.GET)
public SystemInfo getSystemInfo() {
SystemInfo systemInfo = new SystemInfo();
String version = Apollo.VERSION;
if (isValidVersion(version)) {
systemInfo.setVersion(version);
}
List<Env> allEnvList = portalSettings.getAllEnvs();
for (Env env : allEnvList) {
EnvironmentInfo environmentInfo = new EnvironmentInfo();
String metaServerAddresses = MetaDomainConsts.getMetaServerAddress(env);
environmentInfo.setEnv(env);
environmentInfo.setActive(portalSettings.isEnvActive(env));
environmentInfo.setMetaServerAddress(metaServerAddresses);
String selectedMetaServerAddress = MetaDomainConsts.getDomain(env);
try {
environmentInfo.setConfigServices(getServerAddress(selectedMetaServerAddress, CONFIG_SERVICE_URL_PATH));
environmentInfo.setAdminServices(getServerAddress(selectedMetaServerAddress, ADMIN_SERVICE_URL_PATH));
} catch (Throwable ex) {
String errorMessage = "Loading config/admin services from meta server: " + selectedMetaServerAddress + " failed!";
logger.error(errorMessage, ex);
environmentInfo.setErrorMessage(errorMessage + " Exception: " + ex.getMessage());
}
systemInfo.addEnvironment(environmentInfo);
}
return systemInfo;
}
@PreAuthorize(value = "@permissionValidator.isSuperAdmin()")
@RequestMapping(value = "/health", method = RequestMethod.GET)
public Health checkHealth(@RequestParam String host) {
return restTemplate.getForObject(host + "/health", Health.class);
}
private ServiceDTO[] getServerAddress(String metaServerAddress, String path) {
String url = metaServerAddress + path;
return restTemplate.getForObject(url, ServiceDTO[].class);
}
private boolean isValidVersion(String version) {
return !version.equals("java-null");
}
}
package com.ctrip.framework.apollo.portal.entity.vo;
import com.ctrip.framework.apollo.core.dto.ServiceDTO;
import com.ctrip.framework.apollo.core.enums.Env;
public class EnvironmentInfo {
private Env env;
private boolean active;
private String metaServerAddress;
private ServiceDTO[] configServices;
private ServiceDTO[] adminServices;
private String errorMessage;
public Env getEnv() {
return env;
}
public void setEnv(Env env) {
this.env = env;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public String getMetaServerAddress() {
return metaServerAddress;
}
public void setMetaServerAddress(String metaServerAddress) {
this.metaServerAddress = metaServerAddress;
}
public ServiceDTO[] getConfigServices() {
return configServices;
}
public void setConfigServices(ServiceDTO[] configServices) {
this.configServices = configServices;
}
public ServiceDTO[] getAdminServices() {
return adminServices;
}
public void setAdminServices(ServiceDTO[] adminServices) {
this.adminServices = adminServices;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
package com.ctrip.framework.apollo.portal.entity.vo;
import com.google.common.collect.Lists;
import java.util.List;
public class SystemInfo {
private String version;
private List<EnvironmentInfo> environments = Lists.newLinkedList();
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public List<EnvironmentInfo> getEnvironments() {
return environments;
}
public void addEnvironment(EnvironmentInfo environment) {
this.environments.add(environment);
}
}
......@@ -36,3 +36,5 @@ var user_module = angular.module('user', ['apollo.directive', 'toastr', 'app.ser
var login_module = angular.module('login', ['toastr', 'app.util']);
//delete app cluster namespace
var delete_app_cluster_namespace_module = angular.module('delete_app_cluster_namespace', ['app.service', 'apollo.directive', 'app.util', 'toastr', 'angular-loading-bar']);
//system info
var system_info_module = angular.module('system_info', ['app.service', 'apollo.directive', 'app.util', 'toastr', 'angular-loading-bar']);
system_info_module.controller('SystemInfoController',
['$scope', 'toastr', 'AppUtil', 'AppService', 'ClusterService', 'NamespaceService', 'PermissionService', 'SystemInfoService',
SystemInfoController]);
function SystemInfoController($scope, toastr, AppUtil, AppService, ClusterService, NamespaceService, PermissionService, SystemInfoService) {
$scope.systemInfo = {};
$scope.check = check;
initPermission();
function initPermission() {
PermissionService.has_root_permission()
.then(function (result) {
$scope.isRootUser = result.hasPermission;
if (result.hasPermission) {
loadSystemInfo();
}
})
}
function loadSystemInfo() {
SystemInfoService.load_system_info().then(function (result) {
$scope.systemInfo = result;
}, function (result) {
AppUtil.showErrorMsg(result);
});
}
function check(host) {
SystemInfoService.check_health(host).then(function (result) {
var status = result.status.code;
if (status == 'UP') {
toastr.success(host + ' is healthy!');
} else {
toastr.error(host + ' is not healthy, please check ' + host + '/health for more information!');
}
}, function (result) {
AppUtil.showErrorMsg(result);
});
}
}
appService.service('SystemInfoService', ['$resource', '$q', function ($resource, $q) {
var system_info_resource = $resource('', {}, {
load_system_info: {
method: 'GET',
url: '/system-info'
},
check_health: {
method: 'GET',
url: '/system-info/health'
}
});
return {
load_system_info: function () {
var d = $q.defer();
system_info_resource.load_system_info({},
function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
},
check_health: function (host) {
var d = $q.defer();
system_info_resource.check_health({
host: host
},
function (result) {
d.resolve(result);
}, function (result) {
d.reject(result);
});
return d.promise;
}
}
}]);
<!doctype html>
<html ng-app="system_info">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="icon" href="../img/config.png">
<!-- styles -->
<link rel="stylesheet" type="text/css" href="../vendor/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="../vendor/angular/angular-toastr-1.4.1.min.css">
<link rel="stylesheet" type="text/css" media='all' href="../vendor/angular/loading-bar.min.css">
<link rel="stylesheet" type="text/css" href="../styles/common-style.css">
<link rel="stylesheet" type="text/css" href="../vendor/select2/select2.min.css">
<title>系统信息</title>
</head>
<body>
<apollonav></apollonav>
<div class="container-fluid" ng-controller="SystemInfoController">
<div class="col-md-10 col-md-offset-1 panel">
<section class="panel-body" ng-show="isRootUser">
<section class="row">
<h3>系统信息
</h3>
<h6 ng-show="systemInfo.version">系统版本: {{systemInfo.version}}</h6>
<h6>环境列表来自于ApolloPortalDB.ServerConfig中的<strong>apollo.portal.envs</strong>配置,可以到<a href="server_config.html">系统参数</a>页面配置,
更多信息可以参考<a href="https://github.com/ctripcorp/apollo/wiki/%E5%88%86%E5%B8%83%E5%BC%8F%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97">分布式部署指南</a>中的<strong>apollo.portal.envs - 可支持的环境列表</strong>章节。
</h6>
<h6>Meta server地址展示了该环境配置的meta server信息,更多信息可以参考<a href="https://github.com/ctripcorp/apollo/wiki/%E5%88%86%E5%B8%83%E5%BC%8F%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97">分布式部署指南</a>中的<strong>配置apollo-portal的meta service信息</strong>章节。
</h6>
<div ng-repeat="env in systemInfo.environments">
<hr>
<h4>环境: {{env.env}}
</h4>
<h5>Active: {{env.active}}
<span ng-show="env.active == false" style="color: #a94442;">(当前环境状态异常,请结合下方系统信息和Admin Service的Check Health结果排查)</span>
</h5>
<h5>Meta server地址: {{env.metaServerAddress}}</h5>
<div ng-show="env.errorMessage" ng-bind="env.errorMessage" class="alert alert-danger" role="alert">
</div>
<h4 class="text-center">Config Services</h4>
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Instance Id</th>
<th>Home Page Url</th>
<th>Check Health</th>
</tr>
</thead>
<tbody>
<tr ng-show="(!env.configServices || env.configServices.length < 1)">
<td colspan="4">No config service found!</td>
</tr>
<tr ng-show="env.configServices && env.configServices.length > 0" ng-repeat="service in env.configServices">
<td>{{service.appName}}</td>
<td>{{service.instanceId}}</td>
<td>{{service.homepageUrl}}</td>
<td><a href="javascript:;" ng-click="check(service.homepageUrl)">check</a>
</td>
</tr>
</tbody>
</table>
<h4 class="text-center">Admin Services</h4>
<table class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Instance Id</th>
<th>Home Page Url</th>
<th>Check Health</th>
</tr>
</thead>
<tbody>
<tr ng-show="(!env.adminServices || env.adminServices.length < 1)">
<td colspan="4">No admin service found!</td>
</tr>
<tr ng-show="env.adminServices && env.adminServices.length > 0" ng-repeat="service in env.adminServices">
<td>{{service.appName}}</td>
<td>{{service.instanceId}}</td>
<td>{{service.homepageUrl}}</td>
<td><a href="javascript:;" ng-click="check(service.homepageUrl)">check</a>
</tr>
</tbody>
</table>
</div>
</section>
</section>
<section class="panel-body text-center" ng-if="!isRootUser">
<h4>当前页面只对Apollo管理员开放</h4>
</section>
</div>
</div>
<div ng-include="'../views/common/footer.html'"></div>
<!-- jquery.js -->
<script src="../vendor/jquery.min.js" type="text/javascript"></script>
<!--angular-->
<script src="../vendor/angular/angular.min.js"></script>
<script src="../vendor/angular/angular-route.min.js"></script>
<script src="../vendor/angular/angular-resource.min.js"></script>
<script src="../vendor/angular/angular-toastr-1.4.1.tpls.min.js"></script>
<script src="../vendor/angular/loading-bar.min.js"></script>
<!--valdr-->
<script src="../vendor/valdr/valdr.min.js" type="text/javascript"></script>
<script src="../vendor/valdr/valdr-message.min.js" type="text/javascript"></script>
<!-- bootstrap.js -->
<script src="../vendor/bootstrap/js/bootstrap.min.js" type="text/javascript"></script>
<!--nicescroll-->
<script src="../vendor/jquery.nicescroll.min.js"></script>
<script src="../vendor/lodash.min.js"></script>
<script src="../vendor/select2/select2.min.js" type="text/javascript"></script>
<!--biz-->
<!--must import-->
<script type="application/javascript" src="../scripts/app.js"></script>
<script type="application/javascript" src="../scripts/services/AppService.js"></script>
<script type="application/javascript" src="../scripts/services/EnvService.js"></script>
<script type="application/javascript" src="../scripts/services/UserService.js"></script>
<script type="application/javascript" src="../scripts/services/CommonService.js"></script>
<script type="application/javascript" src="../scripts/services/PermissionService.js"></script>
<script type="application/javascript" src="../scripts/services/ClusterService.js"></script>
<script type="application/javascript" src="../scripts/services/NamespaceService.js"></script>
<script type="application/javascript" src="../scripts/services/SystemInfoService.js"></script>
<script type="application/javascript" src="../scripts/AppUtils.js"></script>
<script type="application/javascript" src="../scripts/PageCommon.js"></script>
<script type="application/javascript" src="../scripts/directive/directive.js"></script>
<script type="application/javascript" src="../scripts/valdr.js"></script>
<script type="application/javascript" src="../scripts/controller/SystemInfoController.js"></script>
</body>
</html>
......@@ -34,6 +34,7 @@
<li><a href="/open/manage.html" target="_blank">开放平台授权管理</a></li>
<li><a href="/server_config.html" target="_blank">系统参数</a></li>
<li><a href="/delete_app_cluster_namespace.html" target="_blank">删除应用、集群、AppNamespace</a></li>
<li><a href="/system_info.html" target="_blank">系统信息</a></li>
</ul>
</li>
<li class="dropdown">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment