本文是最后一篇, 本打算写grafana视图以及数据源配置,因为服务器原因,就不写视图配置了,这个也比较简单, 根据官网就可以自己配置出想要的监控视图了.这里写自定义指标(一个用注解实现的,另一个非注解的在github 上,这里就不啰嗦着去写了,实现方式原理相同)以及simple-json的实现;
定义指标
@Component
public class MethodRuntimeCollector extends Collector {
@Override
public List<MetricFamilySamples> collect() {
return Arrays.asList(createGauge("custom_method_runtime_duration","method duration time"));
}
private GaugeMetricFamily createGauge(String metrics, String help) {
GaugeMetricFamily metricFamily = new GaugeMetricFamily(metrics,help,
Collections.singletonList("name"));
for (Map.Entry<String,Double> item : MethodRunTimeService.taskData.entrySet()) {
metricFamily.addMetric(Collections.singletonList(item.getKey()),item.getValue());
}
return metricFamily;
}
}
注册指标
@Configuration
@ConditionalOnClass(CollectorRegistry.class)
public class MethodRuntimeConfig {
private final CollectorRegistry registry;
private MethodRuntimeCollector collector;
public MethodRuntimeConfig(CollectorRegistry registry,MethodRuntimeCollector collector) {
this.registry = registry;
this.collector = collector;
this.collector.register(this.registry);
}
}
指标运算服务
@Component
public class MethodRunTimeService {
public static final Map<String,Double> taskData = new ConcurrentHashMap<>();
public void put(String methodName, Long date){
Assert.notNull(methodName,"方法名不能为空");
Assert.notNull(date,"执行不能为空");
taskData.put(methodName,date2Double(date));
}
private double date2Double(Long date) {
return Double.valueOf(date);
}
}
这里使用的是aop处理方式
定义注解
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface MethodRuntime {
/**
* 方法名称
* @return 方法名 string
*/
String methodName();
}
注解实现
@Aspect
@Component
public class MethodRuntimeAspect {
@Autowired
private MethodRunTimeService methodRunTimeService;
@Pointcut("@annotation(com.surd.monitor.common.annotation.MethodRuntime)")
public void runtime(){}
@Around("runtime()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object obj = joinPoint.proceed();
long endTime = startTime - System.currentTimeMillis();
if (joinPoint.getSignature() instanceof MethodSignature) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String methodName = method.getAnnotation(MethodRuntime.class).methodName();
methodRunTimeService.put(methodName,endTime);
return obj;
} else {
throw new RuntimeException("类型不匹配");
}
}
}
在需要统计的方法上面,添加上@MethodRuntime(methodName = “methodName”) 此注解即可;在grafana视图中就可以配置使用了, 也可以在项目中的指标列表中查看到, 访问地址localhost:8081/monitor/actuator/prometheus
simple-json-datasource 数据接口类
package com.surd.monitor.controller;
import com.surd.monitor.handler.ISimpleJsonData;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Author: GuLang
* @Date: Create in 22:09 2019-08-04
* @Description: 数据提供接口类
* @Version: 1.0
*/
@RestController
public class GrafanaSimpleJson {
@Autowired
@Qualifier("tableDataHandler")
private ISimpleJsonData<List<Map>> simpleJsonData;
public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
public static final String ACCESS_CONTROL_ALLOW_HEADERS_VALUE = "access, content-type";
public static final String ACCESS_CONTROL_ALLOW_METHODS_POST = "POST";
public static final String ACCESS_CONTROL_ALLOW_ORIGIN_VALUE = "*";
public static final String TARTGETS = "targets";
public static final String TARTGET = "target";
@RequestMapping(value = "grafana/", method = {RequestMethod.GET, RequestMethod.POST})
public Map<String, Object> returnTest(HttpServletRequest request, HttpServletResponse response) {
setHeaders(response);
Map<String, Object> map = new HashMap<>(3);
map.put("result", "200 OK");
return map;
}
@RequestMapping(value = "grafana/search", method = {RequestMethod.GET, RequestMethod.POST})
public List search(HttpServletResponse response) {
setHeaders(response);
return null;
}
@RequestMapping(value = "grafana/query", method = {RequestMethod.GET, RequestMethod.POST})
public String query(HttpServletResponse response, @RequestBody Map<String, Object> params) {
setHeaders(response);
List<Map> targetList = (List<Map>) params.get(TARTGETS);
if (targetList.isEmpty() || targetList.get(0).isEmpty() ||
StringUtils.isBlank(String.valueOf(targetList.get(0).get("type")))) {
return null;
}
String type = String.valueOf(targetList.get(0).get("type"));
String resultJson = null;
switch (type) {
case "table":
resultJson = simpleJsonData.getSimpleJson(targetList, type);
break;
default:
break;
}
return resultJson;
}
@RequestMapping(value = "grafana/annotation", method = {RequestMethod.GET, RequestMethod.POST})
public Map annotation(HttpServletResponse response) {
setHeaders(response);
Map<String, Object> map = new HashMap<>();
map.put("result", "200 ok");
return map;
}
private void setHeaders(HttpServletResponse response) {
response.setHeader(ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_HEADERS_VALUE);
response.setHeader(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_POST);
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_ALLOW_ORIGIN_VALUE);
}
}
数据核心处理类(这里监控的是数据库, 所以处理为表格模式)
package com.surd.monitor.handler;
import com.surd.monitor.controller.GrafanaSimpleJson;
import com.surd.monitor.entity.Column;
import com.surd.monitor.entity.MetricsTableVO;
import com.surd.monitor.service.IDbService;
import com.surd.monitor.util.JsonHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Author: GuLang
* @Date: Create in 16:43 2019-08-06
* @Description: 数据处理器
* @Version: 1.0
*/
@Service("tableDataHandler")
public class TableDataHandler extends AbstractDataHandler<MetricsTableVO, List<Map<String, Object>>>
implements ISimpleJsonData<List<Map>> {
@Autowired
private IDbService<List<Map<String, Object>>> dbService;
@Override
public MetricsTableVO handlerData(List<Map<String, Object>> maps, String type) {
Assert.notEmpty(maps, "数据源不能为空");
Assert.notNull(type, "type 不能为空");
MetricsTableVO tableVO = new MetricsTableVO();
List<Column> columns = new ArrayList<>();
List<List<Object>> rows = new ArrayList<>();
tableVO.setType(type);
//取title
columns.addAll(getColumns(maps.get(0)));
//取行数据
rows.addAll(getRows(maps, columns));
tableVO.setColumns(columns);
tableVO.setRows(rows);
return tableVO;
}
private List<List<Object>> getRows(List<Map<String, Object>> maps, List<Column> columns) {
Assert.notEmpty(maps, "数据源不能为空");
Assert.notEmpty(columns, "数据源不能为空");
List<List<Object>> rows = new ArrayList<>();
List<Object> row;
for (Map<String, Object> item : maps) {
row = new ArrayList<>();
for (Column col : columns) {
Object obj = item.get(col.getText());
row.add(String.valueOf(obj));
}
rows.add(row);
}
return rows;
}
private List<Column> getColumns(Map<String, Object> dbSourceInfo) {
Assert.notEmpty(dbSourceInfo, "数据源不能为空");
List<Column> columns = new ArrayList<>();
dbSourceInfo.keySet().forEach(item -> {
Column column = new Column();
column.setText(item);
column.setType("String");
columns.add(column);
});
return columns;
}
/**
* 最终返回结果,用以展示
* @param maps 数据源
* @param type 所需要的类型, 有时间序列,表格等
* @return 字符串
*/
@Override
public String getSimpleJson(List<Map> maps, String type) {
List<Object> result = new ArrayList<>();
for (Map targetMap : maps) {
String target = String.valueOf(targetMap.get(GrafanaSimpleJson.TARTGET));
List<Map<String, Object>> dbInfo = dbService.getDbInfo(target);
MetricsTableVO metricsTableVO = this.handlerData(dbInfo, type);
result.add(metricsTableVO);
}
return JsonHandler.toJsonString(result);
}
}
其他的代码就不粘贴了, 在github上有整个项目的代码;
补充扩展
如果需要其他的数据处理器, 可以自己扩展,继承AbstractDataHandler,实现ISimpleJsonData就可以. 完成后,在GrafanaSimpleJson中添加所要处理的case即可:
@RequestMapping(value = "grafana/query", method = {RequestMethod.GET, RequestMethod.POST})
public String query(HttpServletResponse response, @RequestBody Map<String, Object> params) {
setHeaders(response);
List<Map> targetList = (List<Map>) params.get(TARTGETS);
if (targetList.isEmpty() || targetList.get(0).isEmpty() ||
StringUtils.isBlank(String.valueOf(targetList.get(0).get("type")))) {
return null;
}
String type = String.valueOf(targetList.get(0).get("type"));
String resultJson = null;
switch (type) {
case "table":
resultJson = simpleJsonData.getSimpleJson(targetList, type);
break;
default:
break;
}
return resultJson;
}
后面文章写的比较粗,需要看详细项目的可以上GitHub; 也可以与本人交流.
GitHub: https://github.com/qinyunsurd/monitor