sentine-dashboard集成grafana
利用grafana官方api实现接口:https://grafana.com/docs/grafana/latest/http_api/dashboard/
1 grafana相关配置信息
spring.grafana.url=http://127.0.0.1:3000
spring.grafana.user=user
spring.grafana.password=password
是否开启APIKey 登录验证
spring.grafana.isAPIkey=false
spring.grafana.APIKey.Admin=APIKey
spring.grafana.APIKey.Viewer=APIKey
spring.grafana.APIKey.Editor=APIKey
2 生成APIKey
3 编写GrafanaUtil
/**
* @Auther: shanzhu
* @Date: 2020/10/23 11:53
* @Description: Grafana 工具类
*/
@Component("GrafanaUtil")
public class GrafanaUtil {
/**
* grafana地址
**/
private static String grafanaUrl;
/**
* grafana用户名
**/
private static String username;
/**
* grafana密码
**/
private static String password;
/**
* 是否开启APIKey 登录验证
**/
private static Boolean isAPIkey;
/**
* 最高权限
**/
private static String APIKey_Admin;
/**
* 查看权限
**/
private static String APIKey_Viewer;
/**
* 编辑权限
**/
private static String APIKey_Editor;
@Value("${spring.grafana.url}")
public void setGrafanaUrl(String param) {
grafanaUrl = param;
}
@Value("${spring.grafana.user}")
public void setUsername(String param) {
username = param;
}
@Value("${spring.grafana.password}")
public void setPassword(String param) {
password = param;
}
@Value("${spring.grafana.isAPIkey}")
public void setIsAPIkey(Boolean param) {
isAPIkey = param;
}
@Value("${spring.grafana.APIKey.Admin}")
public void setAPIKey_Admin(String param) {
APIKey_Admin = param;
}
@Value("${spring.grafana.APIKey.Viewer}")
public void setAPIKey_Viewer(String param) {
APIKey_Viewer = param;
}
@Value("${spring.grafana.APIKey.Editor}")
public void setAPIKey_Editor(String param) {
APIKey_Editor = param;
}
public static String getGrafanaUrl() {
return grafanaUrl;
}
/**
* @Author: shanzhu
* @Date: 2020/10/26 15:35
* @Description: 身份验证,是否登录
**/
public static String getAuth(Integer key_num) throws Exception{
HttpResponse<JsonNode> response;
if(isAPIkey){
response = Unirest.get(grafanaUrl+"/api/login/ping")
.header("Accept", "application/json")
.header("Content-Type","application/json")
.header("Authorization",getAPIkey_authHeader(key_num))
.asJson();
}else {
response = Unirest.get(grafanaUrl+"/api/login/ping")
.basicAuth(username,password)
.header("Accept", "application/json")
.header("Content-Type","application/json")
.asJson();
}
return response.getParsingError().get().getOriginalBody();
}
/**
* @Author: shanzhu
* @Date: 2020/10/28 9:46
* @Description: 新增 或者 更新 bashboard
* JSON Body schema:
* dashboard – The complete dashboard model, id = null to create a new dashboard.
* dashboard.id – id = null to create a new dashboard.
* dashboard.uid – Optional unique identifier when creating a dashboard. uid = null will generate a new uid.
* folderId – The id of the folder to save the dashboard in.
* overwrite – Set to true if you want to overwrite existing dashboard with newer version, same dashboard title in folder or same dashboard uid.
* message - Set a commit message for the version history.
* refresh - Set the dashboard refresh interval. If this is lower than the minimum refresh interval, then Grafana will ignore it and will enforce the minimum refresh interval.
* ps:String str = "{\n" +
* " \"dashboard\": {\n" +
* " \"id\": null,\n" +
* " \"uid\": null,\n" +
* " \"title\": \"Production Overview\",\n" +
* " \"tags\": [ \"templated\" ],\n" +
* " \"timezone\": \"browser\",\n" +
* " \"schemaVersion\": 16,\n" +
* " \"version\": 0,\n" +
* " \"refresh\": \"25s\"\n" +
* " },\n" +
* " \"folderId\": 0,\n" +
* " \"overwrite\": false\n" +
* "}";
* @param
*
**/
public static String insertOrUpdateBashboard(String jsonString,Integer key_num) throws Exception{
HttpResponse<JsonNode> response;
if(isAPIkey){
response = Unirest.post(grafanaUrl+"/api/dashboards/db")
.header("Accept", "application/json")
.header("Content-Type","application/json")
.header("Authorization",getAPIkey_authHeader(key_num))
.body(jsonString)
.asJson();
}else {
response = Unirest.post(grafanaUrl+"/api/dashboards/db")
.basicAuth(username,password)
.header("Accept", "application/json")
.header("Content-Type","application/json; charset=UTF-8")
.body(jsonString)
.asJson();
}
return response.getBody().toString();
}
/**
* 导入仪表板
* @param jsonString
* @param key_num
* @return
* @throws Exception
*/
public static String importBashboard(JSONObject jsonString,Integer key_num) throws Exception{
HttpResponse<JsonNode> response;
if(isAPIkey){
response = Unirest.post(grafanaUrl+"api/dashboards/import")
.header("Accept", "application/json")
.header("Content-Type","application/json")
.header("Authorization",getAPIkey_authHeader(key_num))
.body(jsonString)
.asJson();
}else {
response = Unirest.post(grafanaUrl+"api/dashboards/import")
.basicAuth(username,password)
.header("Accept", "application/json")
.header("Content-Type","application/json; charset=UTF-8")
.body(jsonString)
.asJson();
}
return response.getBody().toString();
}
/**
* @Author: shanzhu
* @Date: 2020/10/23 14:54
* @Description: 根据uid获取dashboard
* 测试uid:HYS-BSpGz
**/
public static String getDashboardByUid(String uid,Integer key_num) throws Exception{
HttpResponse<JsonNode> response;
if(isAPIkey){
response = Unirest.get(grafanaUrl+"/api/dashboards/uid/"+uid)
.header("Accept", "application/json")
.header("Content-Type","application/json")
.header("Authorization",getAPIkey_authHeader(key_num))
.asJson();
} else {
response = Unirest.get(grafanaUrl+"/api/dashboards/uid/"+uid)
.basicAuth(username,password)
.header("Accept", "application/json")
.header("Content-Type","application/json")
.asJson();
}
return response.getBody().toString();
}
/**
* @Author: shanzhu
* @Date: 2020/10/27 17:18
* @Description: 搜索Folder/Dashboard
* Query parameters:
* query – Search Query
* tag – List of tags to search for
* type – Type to search for, dash-folder or dash-db
* dashboardIds – List of dashboard id’s to search for
* folderIds – List of folder id’s to search in for dashboards
* starred – Flag indicating if only starred Dashboards should be returned
* limit – Limit the number of returned results (max 5000)
* page – Use this parameter to access hits beyond limit. Numbering starts at 1. limit param acts as page size. Only available in Grafana v6.2+.
* @param paramUrl 查询条件 以?开始 ps:/api/search?folderIds=0&query=&starred=false or /api/search?query=seninel-influxdb-shan-grafana
* @param key_num 权限区分
* @Return 返回查询结果 json格式
**/
public static String searchDashboard(String paramUrl,Integer key_num) throws Exception{
HttpResponse<JsonNode> response;
if(isAPIkey){
response = Unirest.get(grafanaUrl+"/api/search/"+paramUrl)
.header("Accept", "application/json")
.header("Content-Type","application/json")
.header("Authorization",getAPIkey_authHeader(key_num))
.asJson();
} else {
response = Unirest.get(grafanaUrl+"/api/search/"+paramUrl)
.basicAuth(username,password)
.header("Accept", "application/json")
.header("Content-Type","application/json")
.asJson();
}
return response.getBody().toString();
}
/**
* @Author: shanzhu
* @Date: 2020/10/26 14:16
* @Description: 根据权限返回指定的APIKey值
* @param key_num 判断是什么权限:Admin 1 Viewer 2 Editor 3
* 默认为查看权限(Viewer 2)
* @return String APIkey
**/
private static String getAPIkey_authHeader(Integer key_num){
String Bearer = "Bearer ";
if(key_num.intValue() == 1){
return Bearer + APIKey_Admin;
}else if(key_num.intValue() == 2){
return Bearer + APIKey_Viewer;
}else if(key_num.intValue() == 3){
return Bearer + APIKey_Editor;
} else {
// 默认查看权限
return Bearer + APIKey_Viewer;
}
}
}
4 编辑sentinel前端请求接口 app.js中添加接口
this.querySentinelUrlByApp = function(e) {
return l({
url: "/metric/querySentinelUrlByApp.json",
params: e,
method: "GET"
})
}
5 前端实现
$sce’ 需要存在
app.controller('MetricCtl', ['$scope', '$stateParams', 'MetricService', '$interval', '$timeout',**'$sce'**,
接口请求后台获取url,动态改变地址
function () {
var params = {
app: $scope.app
};
MetricService.querySentinelUrlByApp(params).success(function (data) {
$scope.grafanaUrl = $sce.trustAsResourceUrl(data.data);
})
}
html中grafanaUrl则改变地址
<iframe class="report-iframe table-bordered"
target="_blank"
width="1600px"
height="1000px"
seamless
frameBorder="0"
allowfullscreen
ng-src="{{grafanaUrl}}"></iframe>
6 后台实现
/**
* @param app 应用名
* @return Result
* @Author shanzhu
* @Description 根据sentinel应用名查询在grafana中的信息
**/
@ResponseBody
@RequestMapping("/querySentinelUrlByApp.json")
public Result<?> querySentinelUrlByApp(String app){
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app can't be null or empty");
}
String result = "";
try {
ArrayList<Map<String,String>> grafana =
JSON.parseObject(GrafanaUtil.searchDashboard("?query="+app,null), new TypeReference<ArrayList<Map<String,String>>>(){});
// 未查到grafanaDashboard
if (grafana.size() == 0){
// 创建grafana 仪表盘
// file/grafanaDashboard.json 设计好的json数据
ClassPathResource resource = new ClassPathResource("file/grafanaDashboard.json");
InputStream stream = resource.getInputStream();
byte[] byteArr = new byte[stream.available()];
stream.read(byteArr);
String str = new String(byteArr);
String dashboard = GrafanaUtil.insertOrUpdateBashboard(str,1);
JSONObject jsonObject = JSON.parseObject(dashboard);
result = jsonObject.get("url").toString();
} else {
result = grafana.get(0).get("url");
}
result = GrafanaUtil.getGrafanaUrl()+result;
}catch (Exception e){
logger.error("querySentinelUrlByApp:"+e.getMessage());
return Result.ofFail(-1, e.getMessage());
}
return Result.ofSuccess(result);
}
grafanaDashboard.json
{
"dashboard": {
"id": null,
"uid": null,
"title": "Production Overview",
"tags": [ "templated" ],
"timezone": "browser",
"schemaVersion": 16,
"version": 0,
"refresh": "25s"
},
"folderId": 0,
"overwrite": false
}