/* * 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.FetchData; import org.rrd4j.core.FetchRequest; 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.RequestCounter; /** * utility class for logging the request counters into rrd * and plotting graphs * @author timur */ public class RRDRequestCounter { 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(RRDRequestCounter.class); private final RequestCounter counter; 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; /** * * @param rrdDirectory dir where rdd dbs and and images will be created * @param counter the counter logged and plotted by this RRDRequestCounter * @param updatePeriodSecs how often counters will be updated * note that RRDRequestCounter 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 RRDRequestCounter(String rrdDirectory, RequestCounter counter, long updatePeriodSecs ) throws IOException { this(rrdDirectory,counter,updatePeriodSecs,DEFAULT_IMAGE_WIDTH,DEFAULT_IMAGE_HEIGHT); } /** * * @param rrdDirectory dir where rdd dbs and and images will be created * @param counter the counter logged and plotted by this RRDRequestCounter * @param updatePeriodSecs updatePeriodSecs how often counters will be updated * note that RRDRequestCounter does not call update * it relies on external service to call it periodically * @throws IOException */ public RRDRequestCounter(String rrdDirectory, RequestCounter counter, 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("RRDRequestCounter(" + rrdDirectory + ", " + counter + ',' + updatePeriodSecs + ')'); File dir = new File(rrdDirectory); if(!dir.exists() || !dir.isDirectory() || !dir.canWrite() ) { throw new AccessControlException("directory "+ rrdDirectory + " does not exists or is not accessable"); } File imagesDir = new File(dir,"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 counterName = counter.getName(); rrdFiveminImage = rrdImageDir+File.separatorChar+counterName+".5min.png"; rrdHourlyImage = rrdImageDir+File.separatorChar+counterName+".hour.png"; rrdDaylyImage = rrdImageDir+File.separatorChar+counterName+".day.png"; rrdMounthlyImage = rrdImageDir+File.separatorChar+counterName+".month.png"; rrdYearlyImage = rrdImageDir+File.separatorChar+counterName+".year.png"; File f = new File(dir,counterName+".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 // counter resets (restarts) rrdDef.addDatasource("requests",DsType.DERIVE,updatePeriodSecs*2,0, Double.NaN); rrdDef.addDatasource("failed", DsType.DERIVE,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.counter = counter; File html = new File(rrdDirectory,counterName+".html"); if(!html.exists()) { String graphicsHtml = getGraphicsHtml(counterName,imageWidth,imageHeight); FileWriter fw = new FileWriter(html); fw.write(graphicsHtml); fw.close(); } rrdGraphicsHtmlFileName =counterName+".html"; } Long dumpstart; /** * * @throws IOException */ public void update() throws IOException { logger.debug("RRDRequestCounter.update() rrdFileName is "+rrdFileName); RrdDb rrdDb = new RrdDb(rrdFileName); Sample sample = rrdDb.createSample(); long currentTimeSecs = Util.getTime(); StringBuilder sb = new StringBuilder(); sb.append(currentTimeSecs).append(':'); sb.append(counter.getTotalRequests()).append(':'); sb.append(counter.getFailed()); sample.setAndUpdate(sb.toString()); logger.debug("RRDRequestCounter.update() updated with : " + sb); rrdDb.close(); logger.debug("RRDRequestCounter.update() succeeded"); logger.debug("RRDRequestCounter.update() let us try to fetch data"); rrdDb = new RrdDb(rrdFileName); if(dumpstart == null) { dumpstart = currentTimeSecs - 100; } FetchRequest fetchRequest = rrdDb.createFetchRequest(ConsolFun.AVERAGE, dumpstart, currentTimeSecs+1); FetchData fetchData = fetchRequest.fetchData(); logger.debug("RRDRequestCounter.update() dump is: "+fetchData.dump()); rrdDb.close(); } /** * * @throws IOException */ public void graph() throws IOException { long currentTime = Util.getTime(); logger.debug("RRDRequestCounter.graph()"); RrdDb rrdDb = new RrdDb(rrdFileName); RrdGraphDef graphDef = new RrdGraphDef(); graphDef.setVerticalLabel("requests/sec"); graphDef.setUnit("r"); graphDef.datasource("requests_avg", rrdFileName, "requests", ConsolFun.AVERAGE); graphDef.datasource("failed_avg", rrdFileName, "failed", ConsolFun.AVERAGE); graphDef.line("requests_avg", new Color(0xBB, 0, 0), "requests_avg", 4); graphDef.line("failed_avg", new Color(0, 0xBB, 0), "failed_avg", 4); graphDef.datasource("requests_max", rrdFileName, "requests", ConsolFun.MAX); graphDef.datasource("failed_max", rrdFileName, "failed", ConsolFun.MAX); graphDef.line("requests_max", new Color(0xFF, 0, 0), "requests_max", 3); graphDef.line("failed_max", new Color(0, 0xFF, 0), "failed_max", 3); graphDef.datasource("requests_min", rrdFileName, "requests", ConsolFun.MIN); graphDef.datasource("failed_min", rrdFileName, "failed", ConsolFun.MIN); graphDef.line("requests_min", new Color(0x90, 0x20, 0x20), "requests_min", 2); graphDef.line("failed_min", new Color(0x20, 0x90, 0x20), "failed_min", 2); //hour //graphDef.setStartTime(-hour); //#graphDef.setStartTime(-hour); graphDef.setTimeSpan(currentTime-fivemin, currentTime); graphDef.setFilename(rrdFiveminImage); RrdGraph graph = new RrdGraph(graphDef); BufferedImage bi = new BufferedImage(imageWidth,imageHeight,BufferedImage.TYPE_INT_RGB); graph.render(bi.getGraphics()); logger.debug("RRDRequestCounter.graph() wrote "+rrdHourlyImage); graphDef.setTimeSpan(currentTime-hour, currentTime); graphDef.setFilename(rrdHourlyImage); graph = new RrdGraph(graphDef); bi = new BufferedImage(imageWidth,imageHeight,BufferedImage.TYPE_INT_RGB); graph.render(bi.getGraphics()); logger.debug("RRDRequestCounter.graph() wrote "+rrdHourlyImage); //day //graphDef.setStartTime(-day); graphDef.setTimeSpan(currentTime-day, currentTime); graphDef.setFilename(rrdDaylyImage); graph = new RrdGraph(graphDef); bi = new BufferedImage(imageWidth,imageHeight,BufferedImage.TYPE_INT_RGB); graph.render(bi.getGraphics()); //month //graphDef.setStartTime(-month); graphDef.setTimeSpan(currentTime-month, currentTime); graphDef.setFilename(rrdMounthlyImage); graph = new RrdGraph(graphDef); bi = new BufferedImage(imageWidth,imageHeight,BufferedImage.TYPE_INT_RGB); graph.render(bi.getGraphics()); //year //graphDef.setStartTime(-year); graphDef.setTimeSpan(currentTime-year, currentTime); graphDef.setFilename(rrdYearlyImage); graph = new RrdGraph(graphDef); bi = new BufferedImage(imageWidth,imageHeight,BufferedImage.TYPE_INT_RGB); graph.render(bi.getGraphics()); } /** * @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 counterName, int width, int height) { StringBuilder sb = new StringBuilder("\"width: "); sb.append(width); sb.append("px; height: "); sb.append(height); sb.append("px;\""); String style= sb.toString(); sb = new StringBuilder(); sb.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"); sb.append("<html>\n"); sb.append("<head>\n"); sb.append(" <meta content=\"text/html; charset=ISO-8859-1\"\n"); sb.append(" http-equiv=\"content-type\">\n"); sb.append(" <title>Request Rate Graphics for "); sb.append(counterName); sb.append("</title>\n"); sb.append("</head>\n"); sb.append("<body>\n"); sb.append("<h1> Request Rate Graphics for "); sb.append(counterName); sb.append("</h1> \n"); sb.append("5 Minutes<br>\n"); sb.append("<img style=" ); sb.append(style); sb.append(" alt=\"5 Minutes\"\n"); sb.append(" src=\"images/"); sb.append(counterName); sb.append(".5min.png\"><br>\n"); sb.append("Hour<br>\n"); sb.append("<img style=" ); sb.append(style); sb.append(" alt=\"Hour\"\n"); sb.append(" src=\"images/"); sb.append(counterName); sb.append(".hour.png\"><br>\n"); sb.append("Day<br>\n"); sb.append("<img style=" ); sb.append(style); sb.append(" alt=\"Day\"\n"); sb.append(" src=\"images/"); sb.append(counterName); sb.append(".day.png\"><br>\n"); sb.append("Month<br>\n"); sb.append("<img style=" ); sb.append(style); sb.append(" alt=\"Month\"\n"); sb.append(" src=\"images/"); sb.append(counterName); sb.append(".month.png\"><br>\n"); sb.append("Year<br>\n"); sb.append("<img style=" ); sb.append(style); sb.append(" alt=\"Year\"\n"); sb.append(" src=\"images/"); sb.append(counterName); sb.append(".year.png\"><br>\n"); sb.append("</body>\n"); sb.append("</html>"); return sb.toString(); } /** * @return the rrdGraphicsHtmlFileName */ public String getRrdGraphicsHtmlFileName() { return rrdGraphicsHtmlFileName; } public String getName() { return counter.getName(); } }