使用JFreeChart折线图绘制服务器性能监控图

从Prometheus读取监控数据,并使用JFreeChart的拆线图生成对应监控图表。
生成图表效果如下
在这里插入图片描述
实现代码
使用的依赖包:

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-json</artifactId>
            <version>5.8.19</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-http</artifactId>
            <version>5.8.19</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
            <version>5.8.19</version>
        </dependency>
        <dependency>
            <groupId>org.jfree</groupId>
            <artifactId>jfreechart</artifactId>
            <version>1.5.4</version>
        </dependency>

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);
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

penngo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值