All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.rainfall.reporting.HtmlReporter Maven / Gradle / Ivy

There is a newer version: 1.6.10
Show newest version
/*
 * Copyright (c) 2014-2022 Aurélien Broszniowski
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.rainfall.reporting;

import io.rainfall.statistics.StatisticsHolder;
import io.rainfall.statistics.StatisticsPeek;
import io.rainfall.statistics.StatisticsPeekHolder;
import io.rainfall.statistics.collector.StatisticsCollector;
import io.rainfall.statistics.exporter.Exporter;
import io.rainfall.statistics.exporter.HtmlExporter;
import io.rainfall.utils.CompressionUtils;
import org.HdrHistogram.Histogram;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Writer;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;

import static io.rainfall.utils.CompressionUtils.CRLF;
import static io.rainfall.utils.CompressionUtils.cleanFilename;


/**
 * @author Aurelien Broszniowski
 */

public class HtmlReporter> extends FileReporter {

  private String basedir;
  private String averageLatencyFile = "averageLatency.csv";
  private String tpsFile = "tps.csv";
  private String percentilesFile = "total-percentiles.csv";
  private String reportFile;
  private final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());
  private Calendar calendar = GregorianCalendar.getInstance(TimeZone.getDefault());
  private SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
  private CompressionUtils compressionUtils = new CompressionUtils();

  public HtmlReporter() {
    this("target/rainfall-report");
  }

  public HtmlReporter(String outputPath) {
    try {
      this.basedir = new File(outputPath).getAbsoluteFile().getAbsolutePath();
      this.reportPath = new File(this.basedir);
      this.reportFile = this.basedir + File.separatorChar + "report.html";

      compressionUtils.deleteDirectory(new File(this.basedir));

      compressionUtils.extractResources("/report", this.basedir);

      compressionUtils.extractReportTemplateToFile("/template/Tps-template.html", new File(this.reportFile));

    } catch (URISyntaxException e) {
      throw new RuntimeException("Can not read report template");
    } catch (IOException e) {
      throw new RuntimeException("Can not copy report template");
    }
  }

  @Override
  public void header(final List description) {
    StringBuilder sb = new StringBuilder();
    for (String desc : description) {
      sb.append(desc).append("
"); } try { compressionUtils.substituteInFile(reportFile, "//!header!", sb); } catch (Exception e) { throw new RuntimeException("Can not report to Html", e); } } @Override public void report(final StatisticsPeekHolder statisticsPeekHolder) { try { StatisticsPeek totalStatisticsPeeks = statisticsPeekHolder.getTotalStatisticsPeeks(); Set keys = statisticsPeekHolder.getStatisticsPeeksNames(); for (String key : keys) { StatisticsPeek statisticsPeeks = statisticsPeekHolder.getStatisticsPeeks(key); logPeriodicStats(key, statisticsPeeks, statisticsPeekHolder.getResultsReported()); } if (totalStatisticsPeeks != null) logPeriodicStats("total", totalStatisticsPeeks, statisticsPeekHolder.getResultsReported()); logPeriodicExtraStats(statisticsPeekHolder.getExtraCollectedStatistics()); } catch (Exception e) { throw new RuntimeException("Can not write report data", e); } } private void logPeriodicExtraStats(final Map extraCollectedStatistics) throws Exception { for (String statisticsCollectorName : extraCollectedStatistics.keySet()) { ((HtmlExporter)extraCollectedStatistics.get(statisticsCollectorName)).ouputCsv(this.basedir); } } @Override public void summarize(final StatisticsHolder statisticsHolder) { try { copyReportTemplate(statisticsHolder); StringBuilder sb = new StringBuilder(); Enum[] results = statisticsHolder.getResultsReported(); for (Enum result : results) { Histogram histogram = statisticsHolder.fetchHistogram(result); try { histogram = histogram.copyCorrectedForCoordinatedOmission(1000000L); } catch (Throwable t) { // eat it. Sometimes, some places, it throws an exception here. } String percentilesFilename = this.basedir + File.separatorChar + getPercentilesFilename(result.name()); PrintStream stream = new PrintStream(new File(percentilesFilename)); try { histogram.outputPercentileDistribution(stream, 5, 1000000d, true); } catch (Exception e) { e.printStackTrace(); } stream.close(); String mean = "NaN"; try { mean = "" + histogram.getMean(); } catch (Exception e) { e.printStackTrace(); } String maxValue = "NaN"; try { maxValue = "" + histogram.getMaxValue(); } catch (Exception e) { e.printStackTrace(); } sb.append("reportPercentiles('") .append(getPercentilesFilename(result.name()).substring(0, getPercentilesFilename(result.name()).length() - 4)) .append("', 'Response Time percentiles for ").append(result.name()) .append("', '" + mean + "', '" + maxValue) .append("');").append(CRLF); } compressionUtils.substituteInFile(reportFile, "//!summary!", sb); } catch (Exception e) { throw new RuntimeException("Can not report to Html", e); } //TODO : put onglets } private void logPeriodicStats(String name, StatisticsPeek statisticsPeek, final Enum[] resultsReported) throws IOException { String avgFilename = this.basedir + File.separatorChar + getAverageLatencyFilename(name); String tpsFilename = this.basedir + File.separatorChar + getTpsFilename(name); Writer averageLatencyOutput; Writer tpsOutput; averageLatencyOutput = new BufferedWriter(new FileWriter(avgFilename, true)); if (new File(avgFilename).length() == 0) addHeader(averageLatencyOutput, resultsReported); tpsOutput = new BufferedWriter(new FileWriter(tpsFilename, true)); if (new File(tpsFilename).length() == 0) addHeader(tpsOutput, resultsReported); String timestamp = formatTimestampInNano(statisticsPeek.getTimestamp()); StringBuilder averageLatencySb = new StringBuilder(timestamp); StringBuilder tpsSb = new StringBuilder(timestamp); for (Enum result : resultsReported) { averageLatencySb.append(",") .append(String.format("%.4f", (statisticsPeek.getPeriodicAverageLatencyInMs(result)))); tpsSb.append(",").append(statisticsPeek.getPeriodicTps(result)); } averageLatencyOutput.append(averageLatencySb.toString()).append("\n"); tpsOutput.append(tpsSb.toString()).append("\n"); averageLatencyOutput.close(); tpsOutput.close(); } private String getTpsFilename(String key) { return cleanFilename(key) + "-" + this.tpsFile; } private String getAverageLatencyFilename(String key) { return cleanFilename(key) + "-" + this.averageLatencyFile; } private String getPercentilesFilename(String result) { return cleanFilename(result) + "-" + this.percentilesFile; } /** * Define tps/latencies graph in html * TODO : We know what is reported after hand (what domains are reported, e.g. cache1, cache2) * TODO : We should define this during the constructor, we should be able to know what domain is going to be reported */ private void copyReportTemplate(final StatisticsHolder peek) throws IOException { Set names = peek.getStatisticsKeys(); StringBuilder sb = new StringBuilder(); // Periodic for (String name : names) { String tpsFilename = getTpsFilename(name); sb.append("reportTps('").append(tpsFilename.substring(0, tpsFilename.length() - 4)) .append("', 'Periodic TPS - ").append(name) .append("');").append(CRLF); } sb.append("reportTps('total-tps', 'Periodic Total TPS');").append(CRLF); for (String key : names) { String averageLatencyFilename = getAverageLatencyFilename(key); sb.append("reportResponseTime('") .append(averageLatencyFilename.substring(0, averageLatencyFilename.length() - 4)) .append("', 'Periodic Response Time - ").append(key) .append("');").append(CRLF); } sb.append("reportResponseTime('total-averageLatency', 'Periodic Average Response Time of all entities');") .append(CRLF); for (StatisticsCollector statisticsCollector : peek.getStatisticsCollectors()) { sb.append(((HtmlExporter)statisticsCollector.peek()).outputHtml()).append(CRLF); } compressionUtils.substituteInFile(reportFile, "//!report!", sb); } public String formatTimestampInNano(final long timestamp) { calendar.setTime(new Date(timestamp)); return sdf.format(calendar.getTime()); } public void addHeader(Writer output, Enum[] keys) throws IOException { StringBuilder sb = new StringBuilder(); sb.append("timestamp"); for (Enum key : keys) { sb.append(",").append(key.name()); } output.append(sb.toString()).append("\n"); } @Override public String toString() { return "Html reporter (recording to " + this.reportPath + ")"; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy