JSON数据源针对任意后端执行JSON请求。Grafana虽然支持很多数据源,但是有一些特殊的数据处理没办法实现,JSON数据源使得我们更加灵活的组装我们想要的数据。使Grafana不在有数据源的限制,更加灵活。
官网地址:https://grafana.com/grafana/plugins/simpod-json-datasource/
github地址:https://github.com/simPod/GrafanaJsonDatasource
一、安装
1、本地安装,针对通过二进制方式安装的Grafana。
grafana-cli plugins install simpod-json-datasource
【注意】mac在安装完成之后直接重启grafana即可。centos、redhat安装后,执行上面的插件安装命令重启后,看不到新的数据源。因为这个插件安装后的默认位置是/var/lib/grafana/plugins,需要手动将这个插件复制到安装目录的/plugins目录下。因为配置文件中指定了插件的位置,或者将配置文件中插件的位置修改为/var/lib/grafana/plugins。
2、Docker安装
# 进入grafana
docker exec -it grafana bash
# 安装插件
grafana-cli plugins install simpod-json-datasource
# 退出docker
exit
# 将docker中的插件拷贝到宿主机的插件目录下,因为docker启动挂载了宿主机目录
docker cp grafana:/var/lib/grafana/plugins/simpod-json-datasource /Users/guanxin/tools/grafana/plugins
# 查询docker信息
docker ps
# 重启
docker restart <container_id_or_name>
二、添加数据源
登录Grafana,按照一下目录:Home > Connections > Add new connections > JSON
三、接口开发
接口的相关的请求参数和返回参数格式参考官方文档和openapi.yaml文件,地址如下:
https://github.com/simPod/GrafanaJsonDatasource/blob/0.6.x/openapi.yaml
完成 GET / 接口后,点击数据源Save & Test,提示DataSource is working
以下GrafanaController.java的地址要在gateway服务添加放行路由,项目中用的Sa-Token权限认证框架,所以要在网关服务的SaTokenConfigure.java类中添加"/**/grafana/ds/json/**"路由,具体如下图:
GrafanaController.java
@RestController
@RequestMapping(value = "/grafana")
@Api(tags = {"Grafana"})
public class GrafanaController {
@Autowired
private GrafanaJsonDsService grafanaJsonDsService;
@GetMapping(value = "/ds/json/")
@ApiOperation(value = "测试连接", httpMethod = "GET")
public Result testConnect() {
return Result.success();
}
@PostMapping(value = "/ds/json/metrics")
@ApiOperation(value = "指标", httpMethod = "POST")
public JSONArray getMetrics() {
return grafanaJsonDsService.getMetrics();
}
@PostMapping(value = "/ds/json/metric-payload-options")
@ApiOperation(value = "度量有效载荷选项", httpMethod = "POST")
public JSONArray getPayloadOptions() {
return grafanaJsonDsService.getPayloadOptions();
}
@PostMapping(value = "/ds/json/query")
@ApiOperation(value = "查询", httpMethod = "POST")
public JSONArray query(@RequestBody String queryParams) {
return grafanaJsonDsService.query(queryParams);
}
}
GrafanaJsonDsService.java
public interface GrafanaJsonDsService {
/**
* 获取JSON API Grafana Datasource指标
*
* @return 指标集合
*/
JSONArray getMetrics();
/**
* 获取JSON API Grafana Datasource主机选项
* @return
*/
JSONArray getPayloadOptions();
/**
* 获取JSON API Grafana Datasource指标数据
*
* @param queryParams 查询参数
* @return 指标数据集合
*/
JSONArray query(String queryParams);
}
GrafanaJsonDsServiceImpl.java
@Service
public class GrafanaJsonDsServiceImpl implements GrafanaJsonDsService {
@Autowired
private MetricTypeService metricTypeService;
@Autowired
private MonitorMetricService monitorMetricService;
@Autowired
private HostInfoService hostInfoService;
@Autowired
private InfluxDbQueryService influxDbQueryService;
@Autowired
private GrafanaJsonProperties grafanaJsonProperties;
@Override
public JSONArray getMetrics() {
JSONArray result = new JSONArray();
List<MetricType> metricTypeList = metricTypeService.getTypeList();
if (CollUtil.isNotEmpty(metricTypeList)) {
for (MetricType metricType : metricTypeList) {
JSONObject metricsVO = new JSONObject();
metricsVO.put("label", metricType.getName());
metricsVO.put("value", metricType.getName());
JSONArray payloads = new JSONArray();
payloads.add(this.getMetricPayload(metricType));
payloads.add(this.getHostPayload(metricType));
metricsVO.put("payloads", payloads);
result.add(metricsVO);
}
}
return result;
}
@Override
public JSONArray getPayloadOptions() {
JSONArray hostOptions = new JSONArray();
List<HostInfo> hostList = hostInfoService.getHostInfoList();
if (CollUtil.isNotEmpty(hostList)) {
for (HostInfo hostInfo : hostList) {
JSONObject hostOption = new JSONObject();
hostOption.put("label", hostInfo.getHostName());
hostOption.put("value", hostInfo.getInnerIp() + "," + hostInfo.getCloudId());
hostOptions.add(hostOption);
}
}
return hostOptions;
}
@Override
public JSONArray query(String queryParams) {
JSONArray result = new JSONArray();
JSONObject jsonObject = JSON.parseObject(queryParams);
// 步长
String interval = jsonObject.getString("interval");
// 时间
JSONObject range = this.getRawFromAndTo(jsonObject.getString("range"));
// 查询条件
String targets = jsonObject.getString("targets");
List<TargetsDTO> list = JSONArray.parseArray(targets, TargetsDTO.class);
if (CollUtil.isNotEmpty(list)) {
for (TargetsDTO targetsDTO : list) {
InfluxDbData data = null;
String start = (String) range.get("start");
String end = (String) range.get("end");
JSONArray params = this.getQueryMetricsParams(targetsDTO);
if (CollUtil.isNotEmpty(params)) {
for (int i = 0; i < params.size(); i++) {
JSONObject timeSeries = new JSONObject();
JSONObject param = params.getJSONObject(i);
if (null != param.get("formula")) {
data = influxDbQueryService.getMetricDataByPromql((String) param.get("formula"), start, end, interval);
} else if (null != param.get("type")) {
TsQueryParam tsQueryParam = MetricDataUtil.getTsQueryParam(start, end, (String) param.get("ip"), (String) param.get("cloudId"), (Integer) param.get("type"), interval, (String) param.get("metrics"));
data = influxDbQueryService.getMetricChartDataByTs(tsQueryParam);
}
timeSeries.put("target", param.get("target"));
timeSeries.put("datapoints", this.getDataPoints(data));
result.add(timeSeries);
}
}
}
}
return result;
}
/**
* 获取查询指标数据的查询参数
*
* @param targetsDTO JSON API Grafana Datasource Query接口查询条件
*/
private JSONArray getQueryMetricsParams(TargetsDTO targetsDTO) {
JSONArray params = new JSONArray();
String metrics;
String target = targetsDTO.getTarget();
if (StrUtil.isNotBlank(target)) {
JSONObject payload = targetsDTO.getPayload();
metrics = (String) payload.get(target);
MonitorMetric monitorMetric = monitorMetricService.getInfoByCode(metrics);
if (null != monitorMetric) {
String ipAndCloudIdList = payload.getString(target + "Host");
if (StrUtil.isNotBlank(ipAndCloudIdList)) {
JSONArray ipAndCloudIdArray = JSON.parseArray(ipAndCloudIdList);
if (CollUtil.isNotEmpty(ipAndCloudIdArray)) {
for (Object ipAndCloudIdObject : ipAndCloudIdArray) {
JSONObject param = new JSONObject();
String ipAndCloudId = ipAndCloudIdObject.toString();
String ip = ipAndCloudId.split(",")[0];
String cloudId = ipAndCloudId.split(",")[1];
param.put("target", monitorMetric.getName() + "-" + ip);
if (StrUtil.isNotBlank(monitorMetric.getFormula())) {
param.put("formula", monitorMetric.getFormula().replace("$ip", ip).replace("$cloudid", cloudId));
} else {
param.put("ip", ip);
param.put("cloudId", cloudId);
param.put("type", monitorMetric.getType());
param.put("metrics", metrics);
param.put("formula", null);
}
params.add(param);
}
}
}
}
}
return params;
}
/**
* 获取坐标点数据
*
* @param data 指标数据
*/
private JSONArray getDataPoints(InfluxDbData data) {
JSONArray dataPoints = new JSONArray();
if (null != data) {
List<List<String>> valuesList = MetricDataUtil.getMetricsValues(data);
if (CollUtil.isNotEmpty(valuesList)) {
for (List<String> values : valuesList) {
long time = TimeUtil.getUTCDateUnixTimestamp(values.get(0));
double val = 0;
if (null != values.get(1)) {
val = new BigDecimal(values.get(1)).setScale(2, RoundingMode.HALF_UP).stripTrailingZeros().doubleValue();
}
JSONArray dataPoint = new JSONArray();
dataPoint.add(val);
dataPoint.add(time);
dataPoints.add(dataPoint);
}
}
}
return dataPoints;
}
/**
* 获取Raw的开始和结束时间
*
* @param range 时间参数
* @return 转换为简写格式的开始和结束时间
*/
private JSONObject getRawFromAndTo(String range) {
JSONObject fromAndTo = new JSONObject();
if (StrUtil.isNotBlank(range)) {
RangeDTO rangeDTO = JSONArray.parseObject(range, RangeDTO.class);
RawDTO raw = rangeDTO.getRaw();
String start = raw.getFrom().replace("now", "");
String end = raw.getTo().replace("now", "0" + raw.getFrom().substring(raw.getFrom().length() - 1));
fromAndTo.put("start", start);
fromAndTo.put("end", end);
}
return fromAndTo;
}
/**
* 获取指标组下拉框
*
* @param metricType 指标类型
*/
private JSONObject getMetricPayload(MetricType metricType) {
JSONObject metricPayload = new JSONObject();
metricPayload.put("label", metricType.getRemark());
metricPayload.put("name", metricType.getName());
metricPayload.put("type", grafanaJsonProperties.getMetricPayloadType());
metricPayload.put("placeholder", "请选择指标");
metricPayload.put("reloadMetric", Boolean.FALSE);
metricPayload.put("width", grafanaJsonProperties.getPayloadWidth());
metricPayload.put("options", this.getMetricOptions(metricType));
return metricPayload;
}
/**
* 获取指标组下拉框的指标项
*
* @param metricType 指标类型
* @return 指标项集合
*/
private JSONArray getMetricOptions(MetricType metricType) {
JSONArray metricOptions = new JSONArray();
List<MonitorMetric> monitorMetricList = monitorMetricService.getMetricByTypeId(metricType.getUuid());
if (CollUtil.isNotEmpty(monitorMetricList)) {
for (MonitorMetric monitorMetric : monitorMetricList) {
JSONObject metricOption = new JSONObject();
metricOption.put("label", monitorMetric.getName());
metricOption.put("value", monitorMetric.getCode());
metricOptions.add(metricOption);
}
}
return metricOptions;
}
/**
* 获取主机下拉框
*/
private JSONObject getHostPayload(MetricType metricType) {
JSONObject hostPayload = new JSONObject();
hostPayload.put("label", metricType.getName() + "主机");
hostPayload.put("name", metricType.getName() + "Host");
hostPayload.put("type", grafanaJsonProperties.getHostPayloadType());
hostPayload.put("placeholder", "请选择主机");
hostPayload.put("reloadMetric", Boolean.FALSE);
hostPayload.put("width", grafanaJsonProperties.getPayloadWidth());
return hostPayload;
}
}
GrafanaJsonProperties.java
@Data
@Configuration
@ConfigurationProperties("grafana.json")
public class GrafanaJsonProperties {
/**
* 指标选择框类型(单选)
*/
private String metricPayloadType;
/**
* 主机选择框类型(多选)
*/
private String hostPayloadType;
/**
* 指标及主机选择框宽度
*/
private Integer payloadWidth;
}
配置文件信息
grafana:
json:
metricPayloadType: select
hostPayloadType: multi-select
payloadWidth: 80