从Prometheus读取监控数据,并使用JFreeChart的拆线图生成对应监控图表。
生成图表效果如下
实现代码
使用的依赖包:
cn.hutool
hutool-json
5.8.19
cn.hutool
hutool-http
5.8.19
cn.hutool
hutool-core
5.8.19
org.jfree
jfreechart
1.5.4
SystemChart.java生成折线图操作类
import cn.hutool.json.JSONObject;
import com.penngo.prometheus.component.PromMetric;
import com.penngo.prometheus.component.UrlInfo;
import org.jfree.chart.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.chart.title.TextTitle;
import org.jfree.chart.ui.*;
import org.jfree.data.time.*;
import org.jfree.data.xy.XYDataset;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.text.DateFormat;
import java.time.LocalDateTime;
import java.util.List;
import java.util.*;
public class SystemChart {
private static Logger log = LoggerFactory.getLogger(SystemChart.class);
public static void createMetricChart(List<PromMetric> metricList, UrlInfo urlInfo, long start, long end){
TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
for(PromMetric promMetric:metricList){
TimeSeries timeSeries = createTimeSeries(promMetric);
timeseriescollection.addSeries(timeSeries);
}
JFreeChart freeChart = createChart(timeseriescollection, urlInfo, start, end);
File img = new File("logs/"+urlInfo.getKey()+".png");
// ImageUtil.saveImage(freeChart, img, 1920, 1080);
ImageUtil.saveImage(freeChart, img, 1024, 512);
}
private static TimeSeries createTimeSeries(PromMetric promMetric){
JSONObject metric = promMetric.getMetric();
List<List<Object>> valueList = promMetric.getValues();
String instance = metric.getStr("instance");
int instanceIndex = instance.lastIndexOf(":");
instance = instanceIndex > -1 ? instance.substring(0, instanceIndex) : instance;
String name = instance;
TimeSeries timeseries = new TimeSeries(name);
LocalDateTime localDateTime = null;
for(List<Object> metricValue:valueList){
long time = Double.valueOf(metricValue.get(0).toString()).longValue();
double value = Double.valueOf(metricValue.get(1).toString());
localDateTime = DateUtil.secondToLocalDateTime(time);
Day day = new Day(DateUtil.localDateTimeToDate(localDateTime));
int hour = localDateTime.getHour();
int minute = localDateTime.getMinute();
timeseries.add(new Minute(minute, new Hour(hour, day)), value);
}
return timeseries;
}
private static JFreeChart createChart(XYDataset xydataset,UrlInfo urlInfo, long start, long end)
{
LocalDateTime startDate = DateUtil.secondToLocalDateTime(start);
LocalDateTime endDate = DateUtil.secondToLocalDateTime(end);
createTheme();
JFreeChart jfreechart = ChartFactory.createTimeSeriesChart(urlInfo.getTitle(), "", "", xydataset, false, true, false);
Font titleFont = new Font("宋体", Font.BOLD, 20);
jfreechart.getTitle().setFont(titleFont);
// 创建子标题
createSubTitle(jfreechart, urlInfo);
jfreechart.setBorderVisible(false);
XYPlot plot = (XYPlot) jfreechart.getPlot();
plot.setBackgroundPaint(Color.WHITE);
plot.setDomainGridlinePaint(Color.lightGray);
plot.setRangeGridlinePaint(Color.lightGray);
plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 20.0));
plot.setDomainCrosshairVisible(true);
plot.setRangeCrosshairVisible(true);
plot.setInsets(new RectangleInsets(5.0D, 5.0D, 5.0D, 20.0D));
// 创建折线条标签
createLegend(jfreechart, plot, xydataset);
// 线条颜色
XYItemRenderer r = plot.getRenderer();
if (r instanceof XYLineAndShapeRenderer) {
XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) r;
for(int i = 0; i < xydataset.getSeriesCount(); i++){
renderer.setSeriesPaint(i, ColorUtils.colorlist.get(i).getColor());
renderer.setSeriesStroke(i, new BasicStroke(2));
}
}
// X轴
plot.setDomainAxis(new CustomDateAxis(start, end));
DateAxis dateAxis = (DateAxis) plot.getDomainAxis();
// 设备X轴时间显示最大和最小值
dateAxis.setRange(DateUtil.localDateTimeToDate(startDate), DateUtil.localDateTimeToDate(endDate));
// Y轴
plot.setRangeAxis(new CustomNumberAxis());
NumberAxis valueAxis = (NumberAxis)plot.getRangeAxis();
valueAxis.setRange(0,105);
// 分割线
createRangeMarker(plot);
return jfreechart;
}
private static void createRangeMarker(XYPlot plot){
ValueMarker vmarker = new ValueMarker(80d);//设置分割线
vmarker.setLabel("使用率超过80%性能预警");
vmarker.setLabelPaint(Color.red);
vmarker.setPaint(Color.red);
vmarker.setStroke(new BasicStroke(1.0f,BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,
1.0f,new float[]{3,3,},0f));
vmarker.setLabelFont(new Font("宋体",Font.PLAIN,12));
vmarker.setLabelAnchor(RectangleAnchor.BOTTOM_RIGHT);
vmarker.setLabelTextAnchor(TextAnchor.TOP_RIGHT);
plot.addRangeMarker(vmarker);
}
private static void createTheme(){
StandardChartTheme standardChartTheme = new StandardChartTheme("JFree"); //或者为Legacy
standardChartTheme.setRegularFont(new Font("宋体", Font.PLAIN, 16));
standardChartTheme.setExtraLargeFont(new Font("宋体", Font.BOLD, 20));
standardChartTheme.setSmallFont(new Font("宋体", Font.PLAIN, 16));
standardChartTheme.setLargeFont(new Font("宋体", Font.PLAIN, 16));
ChartFactory.setChartTheme(standardChartTheme);
}
/**
* 出创建折线条标签
* @param plot
*/
private static void createLegend(JFreeChart jfreechart, XYPlot plot, XYDataset xydataset){
Font itemFont = new Font("宋体", Font.BOLD, 16);
LegendItemCollection legendItemCollection = new LegendItemCollection();
List<TimeSeries> serieList = ((TimeSeriesCollection)xydataset).getSeries();
for(int i = 0; i < serieList.size(); i++){
TimeSeries series = serieList.get(i);
LegendItem legendItem = new LegendItem(series.getKey().toString(), null, null, null, new Rectangle2D.Double(-6.0D, -3.0D, 18.0D, 18.0D), ColorUtils.colorlist.get(i).getColor());
legendItem.setLabelFont(itemFont);
legendItemCollection.add(legendItem);
}
plot.setFixedLegendItems(legendItemCollection);
LegendTitle var6 = new LegendTitle(plot);
var6.setPosition(RectangleEdge.BOTTOM);
jfreechart.addSubtitle(var6);
}
private static void createSubTitle(JFreeChart jfreechart, UrlInfo urlInfo){
Font subTitleFont = new Font("宋体", Font.BOLD, 12);
TextTitle subtitle1 = new TextTitle("日期:" + DateUtil.getNowStr("yyyy-MM-dd HH:mm:ss"));
subtitle1.setPaint(new Color(144,144,144));
subtitle1.setFont(subTitleFont);
subtitle1.setPosition(RectangleEdge.TOP);
subtitle1.setHorizontalAlignment(HorizontalAlignment.RIGHT);
subtitle1.setMargin(0.0D, 0.0D, 0.0D, 50.0D);
jfreechart.addSubtitle(subtitle1);
TextTitle subtitle2 = new TextTitle(urlInfo.getSubTitle());
subtitle2.setPaint(new Color(144,144,144));
subtitle2.setFont(subTitleFont);
subtitle2.setPosition(RectangleEdge.BOTTOM);
subtitle2.setHorizontalAlignment(HorizontalAlignment.CENTER);
subtitle2.setMargin(4.0D, 0.0D, 2.0D, 4.0D);
jfreechart.addSubtitle(subtitle2);
TextTitle subtitle3 = new TextTitle("百分比");
subtitle3.setPaint(new Color(144,144,144));
subtitle3.setFont(subTitleFont);
subtitle3.setPosition(RectangleEdge.TOP);
subtitle3.setHorizontalAlignment(HorizontalAlignment.LEFT);
subtitle3.setMargin(-16.0D, 20.0D, 2.0D, 4.0D);
jfreechart.addSubtitle(subtitle3);
}
private static class CustomNumberAxis extends NumberAxis{
public List refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) {
List result = new ArrayList();
for(int number = 0; number <= 100; number += 5){
result.add(new NumberTick(TickType.MINOR, number, number + "%", TextAnchor.CENTER_RIGHT, TextAnchor.CENTER, 0.0));
}
return result;
}
}
private static class CustomDateAxis extends DateAxis{
private long start;
private long end;
public CustomDateAxis(long start, long end){
this.start = start;
this.end = end;
}
public List refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) {
List result = new ArrayList();
if (RectangleEdge.isTopOrBottom(edge)) {
Font tickLabelFont = this.getTickLabelFont();
g2.setFont(tickLabelFont);
if (this.isAutoTickUnitSelection()) {
this.selectAutoTickUnit(g2, dataArea, edge);
}
DateTickUnit unit = this.getTickUnit();
Date tickDate = this.calculateLowestVisibleTickValue(unit);
for(long index = start; index <= end; index += 3600){
LocalDateTime localDateTime = DateUtil.secondToLocalDateTime(index);
Date date = DateUtil.localDateTimeToDate(localDateTime);
String timeValue = DateUtil.dateToStr(localDateTime, "HH:mm");
Tick tick = new DateTick(date, timeValue, TextAnchor.TOP_CENTER, TextAnchor.TOP_CENTER, 0.0);
result.add(tick);
}
} else if (RectangleEdge.isLeftOrRight(edge)) {
result = this.refreshTicksVertical(g2, dataArea, edge);
}
return result;
}
protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) {
List result = new ArrayList();
Font tickLabelFont = this.getTickLabelFont();
g2.setFont(tickLabelFont);
if (this.isAutoTickUnitSelection()) {
this.selectAutoTickUnit(g2, dataArea, edge);
}
DateTickUnit unit = this.getTickUnit();
Date tickDate = this.calculateLowestVisibleTickValue(unit);
Date upperDate = this.getMaximumDate();
boolean hasRolled = false;
while(true) {
while(tickDate.before(upperDate)) {
long lowestTickTime = tickDate.getTime();
long distance = unit.addToDate(tickDate, this.getTimeZone()).getTime() - lowestTickTime;
int minorTickSpaces = this.getMinorTickCount();
if (minorTickSpaces <= 0) {
minorTickSpaces = unit.getMinorTickCount();
}
for(int minorTick = 1; minorTick < minorTickSpaces; ++minorTick) {
long minorTickTime = lowestTickTime - distance * (long)minorTick / (long)minorTickSpaces;
if (minorTickTime > 0L && this.getRange().contains((double)minorTickTime) && !this.isHiddenValue(minorTickTime)) {
result.add(new DateTick(TickType.MINOR, new Date(minorTickTime), "", TextAnchor.TOP_CENTER, TextAnchor.CENTER, 0.0D));
}
}
if (!this.isHiddenValue(tickDate.getTime())) {
DateFormat formatter = this.getDateFormatOverride();
String tickLabel;
if (formatter != null) {
tickLabel = formatter.format(tickDate);
} else {
tickLabel = this.getTickUnit().dateToString(tickDate);
}
double angle = 0.0D;
TextAnchor anchor;
TextAnchor rotationAnchor;
if (this.isVerticalTickLabels()) {
anchor = TextAnchor.CENTER_RIGHT;
rotationAnchor = TextAnchor.CENTER_RIGHT;
if (edge == RectangleEdge.TOP) {
angle = 1.5707963267948966D;
} else {
angle = -1.5707963267948966D;
}
} else if (edge == RectangleEdge.TOP) {
anchor = TextAnchor.BOTTOM_CENTER;
rotationAnchor = TextAnchor.BOTTOM_CENTER;
} else {
anchor = TextAnchor.TOP_CENTER;
rotationAnchor = TextAnchor.TOP_CENTER;
}
Tick tick = new DateTick(tickDate, tickLabel, anchor, rotationAnchor, angle);
result.add(tick);
hasRolled = false;
long currentTickTime = tickDate.getTime();
tickDate = unit.addToDate(tickDate, this.getTimeZone());
long nextTickTime = tickDate.getTime();
for(int minorTick = 1; minorTick < minorTickSpaces; ++minorTick) {
long minorTickTime = currentTickTime + (nextTickTime - currentTickTime) * (long)minorTick / (long)minorTickSpaces;
if (this.getRange().contains((double)minorTickTime) && !this.isHiddenValue(minorTickTime)) {
result.add(new DateTick(TickType.MINOR, new Date(minorTickTime), "", TextAnchor.TOP_CENTER, TextAnchor.CENTER, 0.0D));
}
}
} else {
tickDate = unit.rollDate(tickDate, this.getTimeZone());
hasRolled = true;
}
}
return result;
}
}
}
}
public class PrometheusChart {
private static Logger log = LoggerFactory.getLogger(PrometheusCommand.class);
private final static String server = "http://192.168.28.127:9090/api/v1/query_range?";
@Override
public void run(String... args){
try{
long end = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
long start = end - 24 * 3600;
List<UrlInfo> urlList = Arrays.asList(
new UrlInfo("test_cpu", "CPU使用率", "服务器巡检报告","query=process_system_cpu%7Bapp%3D%7E%22city_.*%22%7D&start="+ start + ".453&end=" + end + ".453&step=345")
);
for(UrlInfo urlInfo: urlList) {
log.info("url====" + HttpUtil.createGet(server).form(urlInfo.getParame()).getUrl());
String result = HttpUtil.get(server + urlInfo.getParame());
PromResult promResult = JSONUtil.toBean(result, PromResult.class);
log.info("json====" + JSONUtil.toJsonStr(promResult));
if(promResult != null && promResult.getStatus().equals("success")){
PromData data = promResult.getData();
if(data != null && data.getResult() != null){
List<PromMetric> metricList = data.getResult();
SystemChart.createMetricChart(metricList, urlInfo, start, end);
}
}
}
}
catch(Exception e){
log.error("run error====", e);
}
}
}