/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.dcache.commons.stats.rrd; import org.rrd4j.ConsolFun; import org.rrd4j.DsType; import org.rrd4j.core.RrdDb; import org.rrd4j.core.RrdDef; import org.rrd4j.core.Sample; import org.rrd4j.core.Util; import org.rrd4j.graph.RrdGraph; import org.rrd4j.graph.RrdGraphDef; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.security.AccessControlException; import java.util.concurrent.TimeUnit; import org.dcache.commons.stats.RequestExecutionTimeGauge; /** * utility class for logging the request gauges into rrd * and plotting graphs * @author timur */ public class RRDRequestExecutionTimeGauge { private static final long FIVEMIN = TimeUnit.MINUTES.toSeconds( 5); private static final long TENMIN = TimeUnit.MINUTES.toSeconds( 5); private static final long HOUR = TimeUnit.HOURS.toSeconds(1); private static final long DAY = TimeUnit.DAYS.toSeconds(1); private static final long MONTH = TimeUnit.DAYS.toSeconds(31); private static final long YEAR = TimeUnit.DAYS.toSeconds(365); private static final int DEFAULT_IMAGE_WIDTH=491; private static final int DEFAULT_IMAGE_HEIGHT=167; private static final Logger logger = LoggerFactory.getLogger(RRDRequestExecutionTimeGauge.class); private final RequestExecutionTimeGauge gauge; private final String rrdFileName; private final String rrdFiveminImage; private final String rrdHourlyImage; private final String rrdDaylyImage; private final String rrdMounthlyImage; private final String rrdYearlyImage; private final String rrdGraphicsHtmlFileName; private int imageWidth = DEFAULT_IMAGE_WIDTH; private int imageHeight = DEFAULT_IMAGE_HEIGHT; private Long dumpstart; /** * * @param rrdDirectory dir where rdd dbs and and images will be created * @param gauge the gauge logged and plotted by this RRDRequestExecutionTimeGauge * @param updatePeriodSecs how often gauges will be updated * note that RRDRequestExecutionTimeGauge does not call update * it relies on external service to call it periodically * @param imageWidth width of the images generated * @param imageHeight height of the images generated * @throws IOException */ public RRDRequestExecutionTimeGauge(File rrdDirectory, RequestExecutionTimeGauge gauge, long updatePeriodSecs ) throws IOException { this(rrdDirectory,gauge,updatePeriodSecs,DEFAULT_IMAGE_WIDTH,DEFAULT_IMAGE_HEIGHT); } /** * * @param rrdDirectory dir where rdd dbs and and images will be created * @param gauge the gauge logged and plotted by this RRDRequestExecutionTimeGauge * @param updatePeriodSecs updatePeriodSecs how often gauges will be updated * note that RRDRequestExecutionTimeGauge does not call update * it relies on external service to call it periodically * @throws IOException */ public RRDRequestExecutionTimeGauge(File rrdDirectory, RequestExecutionTimeGauge gauge, long updatePeriodSecs,int imageWidth, int imageHeight) throws IOException { this.imageWidth = imageWidth; this.imageHeight = imageHeight; if(updatePeriodSecs <=0 || updatePeriodSecs >=FIVEMIN) { throw new IllegalArgumentException("updatePeriodSecs="+updatePeriodSecs+ ", should be greater than 0 and less than "+FIVEMIN+" secs"); } logger.debug("RRDRequestExecutionTimeGauge(" + rrdDirectory + ", " + gauge + ',' + updatePeriodSecs + ')'); if(!rrdDirectory.exists() || !rrdDirectory.isDirectory() || !rrdDirectory.canWrite() ) { throw new AccessControlException("directory "+ rrdDirectory + " does not exists or is not accessable"); } File imagesDir = new File(rrdDirectory,"images"); if(!imagesDir.exists()) { imagesDir.mkdir() ; } if(!imagesDir.exists() || !imagesDir.isDirectory() || !imagesDir.canWrite() ) { throw new AccessControlException("directory "+ imagesDir + " does not exists or is not accessable"); } String rrdImageDir = imagesDir.getCanonicalPath(); String gaugeName = gauge.getName(); rrdFiveminImage = rrdImageDir+File.separatorChar+gaugeName+".5min.png"; rrdHourlyImage = rrdImageDir+File.separatorChar+gaugeName+".hour.png"; rrdDaylyImage = rrdImageDir+File.separatorChar+gaugeName+".day.png"; rrdMounthlyImage = rrdImageDir+File.separatorChar+gaugeName+".month.png"; rrdYearlyImage = rrdImageDir+File.separatorChar+gaugeName+".year.png"; File f = new File(rrdDirectory,gaugeName+".rrd4j"); this.rrdFileName = f.getCanonicalPath(); if(!f.exists()) { RrdDef rrdDef = new RrdDef(this.rrdFileName); rrdDef.setStartTime( Util.getTime()-1); rrdDef.setStep(updatePeriodSecs); // step is 60 seconds or one minute // use derive to eliminate the false jumps in values due to // gauge resets (restarts) rrdDef.addDatasource("exectime",DsType.GAUGE,updatePeriodSecs*2,0, Double.NaN); //one sample for every period of updatePeriodSecs // for one hour int samplesPerOur =(int) (HOUR / updatePeriodSecs ); rrdDef.addArchive(ConsolFun.AVERAGE, 0.5d, 1, samplesPerOur); rrdDef.addArchive(ConsolFun.MIN, 0.5d, 1, samplesPerOur); rrdDef.addArchive(ConsolFun.MAX, 0.5d, 1, samplesPerOur); //sample for every 10 min period of updatePeriodSecs // for 100 hours int samplesPerTenMin = (int) (TENMIN / updatePeriodSecs); int tenMinSamplesPer100Hours =(int)(HOUR*100/samplesPerTenMin); rrdDef.addArchive(ConsolFun.AVERAGE, 0.5d, samplesPerTenMin, tenMinSamplesPer100Hours); rrdDef.addArchive(ConsolFun.MIN, 0.5d, samplesPerTenMin, tenMinSamplesPer100Hours); rrdDef.addArchive(ConsolFun.MAX, 0.5d, samplesPerTenMin, tenMinSamplesPer100Hours); //sample for every 1 hour period of updatePeriodSecs // for one month int hourSamplesPerMonth =(int)(MONTH/samplesPerOur); rrdDef.addArchive(ConsolFun.AVERAGE, 0.5d, samplesPerOur, hourSamplesPerMonth); rrdDef.addArchive(ConsolFun.MIN, 0.5d, samplesPerOur, hourSamplesPerMonth); rrdDef.addArchive(ConsolFun.MAX, 0.5d, samplesPerOur, hourSamplesPerMonth); //one sample for every one day period of updatePeriodSecs // for two years int samplesPerDay =(int) (24*HOUR / updatePeriodSecs ); int daySamplesPer2Years =(int)(2*YEAR/samplesPerDay); rrdDef.addArchive(ConsolFun.AVERAGE, 0.5d, samplesPerDay, daySamplesPer2Years); rrdDef.addArchive(ConsolFun.MIN, 0.5d, samplesPerDay, daySamplesPer2Years); rrdDef.addArchive(ConsolFun.MAX, 0.5d, samplesPerDay, daySamplesPer2Years); RrdDb rrdDb = new RrdDb(rrdDef); rrdDb.close(); } RrdDb rrdDb = new RrdDb(this.rrdFileName);// check that we can read it // we could use rrdDb.getRrdDef() to make sure that this is a correct rrd rrdDb.close(); this.gauge = gauge; File html = new File(rrdDirectory,gaugeName+".html"); if(!html.exists()) { String graphicsHtml = getGraphicsHtml(gaugeName,imageWidth,imageHeight); FileWriter fw = new FileWriter(html); fw.write(graphicsHtml); fw.close(); } rrdGraphicsHtmlFileName =gaugeName+".html"; } /** * Performs update of the rrd with the current value of the gauge * @throws IOException */ public void update() throws IOException { logger.debug("RRDRequestExecutionTimeGauge.update() rrdFileName is "+rrdFileName); RrdDb rrdDb = new RrdDb(rrdFileName); try { Sample sample = rrdDb.createSample(); long currentTimeSecs = Util.getTime(); String update = Long.toString(currentTimeSecs) +':'+ (long) gauge.resetAndGetAverageExecutionTime()+':'; sample.setAndUpdate(update); logger.debug("RRDRequestExecutionTimeGauge.update() updated with : "+update); } finally { rrdDb.close(); logger.debug("RRDRequestExecutionTimeGauge.update() succeeded"); } } private void plotGraph(RrdGraphDef graphDef, long beginningtime, long endTime, String filename ) throws IOException { graphDef.setTimeSpan(beginningtime, endTime); graphDef.setFilename(filename); RrdGraph graph = new RrdGraph(graphDef); BufferedImage bi = new BufferedImage(imageWidth,imageHeight,BufferedImage.TYPE_INT_RGB); graph.render(bi.getGraphics()); logger.debug("RRDRequestExecutionTimeGauge.graph() wrote "+filename); } /** * Plots up to date graphs of the gauge * @throws IOException */ public void graph() throws IOException { long currentTime = Util.getTime(); logger.debug("RRDRequestExecutionTimeGauge.graph()"); RrdDb rrdDb = new RrdDb(rrdFileName); RrdGraphDef graphDef = new RrdGraphDef(); graphDef.setVerticalLabel("exectime(ms)"); graphDef.setUnit("ms"); graphDef.datasource("exectime_avg", rrdFileName, "exectime", ConsolFun.AVERAGE); graphDef.line("exectime_avg", new Color(0xBB, 0, 0), "exectime_avg", 4); graphDef.datasource("exectime_max", rrdFileName, "exectime", ConsolFun.MAX); graphDef.line("exectime_max", new Color(0xFF, 0, 0), "exectime_max", 3); graphDef.datasource("exectime_min", rrdFileName, "exectime", ConsolFun.MIN); graphDef.line("exectime_min", new Color(0x90, 0x20, 0x20), "exectime_min", 2); //hour //graphDef.setStartTime(-hour); //#graphDef.setStartTime(-hour); plotGraph(graphDef,currentTime-FIVEMIN,currentTime,rrdFiveminImage); plotGraph(graphDef,currentTime-HOUR,currentTime,rrdHourlyImage); plotGraph(graphDef,currentTime-DAY,currentTime,rrdDaylyImage); plotGraph(graphDef,currentTime-MONTH,currentTime,rrdMounthlyImage); plotGraph(graphDef,currentTime-YEAR,currentTime,rrdYearlyImage); } /** * @return the imageWidth */ public int getImageWidth() { return imageWidth; } /** * @param imageWidth the imageWidth to set */ public void setImageWidth(int imageWidth) { this.imageWidth = imageWidth; } /** * @return the imageHight */ public int getImageHight() { return imageHeight; } /** * @param imageHight the imageHight to set */ public void setImageHight(int imageHight) { this.imageHeight = imageHight; } private static String getGraphicsHtml( String gaugeName, int width, int height) { StringBuilder style = new StringBuilder("\"width: "); style.append(width); style.append("px; height: "); style.append(height); style.append("px;\""); String sb = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" + "<html>\n" + "<head>\n" + " <meta content=\"text/html; charset=ISO-8859-1\"\n" + " http-equiv=\"content-type\">\n" + " <title>Request Execution Time Graphics for " + gaugeName + "</title>\n" + "</head>\n" + "<body>\n" + "<h1> Request Execution Time Graphics for " + gaugeName + "</h1> \n" + "5 Minutes<br>\n" + "<img style=" + style + " alt=\"5 Minutes\"\n" + " src=\"images/" + gaugeName + ".5min.png\"><br>\n" + "Hour<br>\n" + "<img style=" + style + " alt=\"Hour\"\n" + " src=\"images/" + gaugeName + ".hour.png\"><br>\n" + "Day<br>\n" + "<img style=" + style + " alt=\"Day\"\n" + " src=\"images/" + gaugeName + ".day.png\"><br>\n" + "Month<br>\n" + "<img style=" + style + " alt=\"Month\"\n" + " src=\"images/" + gaugeName + ".month.png\"><br>\n" + "Year<br>\n" + "<img style=" + style + " alt=\"Year\"\n" + " src=\"images/" + gaugeName + ".year.png\"><br>\n" + "</body>\n" + "</html>"; return sb; } /** * @return the rrdGraphicsHtmlFileName */ public String getRrdGraphicsHtmlFileName() { return rrdGraphicsHtmlFileName; } }