/*
* Copyright 2008-2017 by Emeric Vernat
*
* This file is part of Java Melody.
*
* 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 net.bull.javamelody;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.lowagie.text.BadElementException;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Image;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
/**
* Partie du rapport pdf pour un compteur.
* @author Emeric Vernat
*/
class PdfCounterReport extends PdfAbstractTableReport {
private final Collector collector;
private final Counter counter;
private final Range range;
private final boolean includeGraph;
private final CounterRequestAggregation counterRequestAggregation;
private final DecimalFormat systemErrorFormat = I18N.createPercentFormat();
private final DecimalFormat integerFormat = I18N.createIntegerFormat();
private final Font infoCellFont = PdfFonts.INFO_CELL.getFont();
private final Font warningCellFont = PdfFonts.WARNING_CELL.getFont();
private final Font severeCellFont = PdfFonts.SEVERE_CELL.getFont();
private final Font normalFont = PdfFonts.NORMAL.getFont();
PdfCounterReport(Collector collector, Counter counter, Range range, boolean includeGraph,
Document document) {
super(document);
assert collector != null;
assert counter != null;
assert range != null;
this.collector = collector;
this.counter = counter;
this.range = range;
this.includeGraph = includeGraph;
this.counterRequestAggregation = new CounterRequestAggregation(counter);
}
@Override
void toPdf() throws DocumentException, IOException {
final List<CounterRequest> requests = counterRequestAggregation.getRequests();
if (requests.isEmpty()) {
writeNoRequests();
} else if (isErrorAndNotJobCounter()) {
// il y a au moins une "request" d'erreur puisque la liste n'est pas vide
assert !requests.isEmpty();
final List<CounterRequest> summaryRequest = Collections.singletonList(requests.get(0));
writeRequests(counter.getChildCounterName(), summaryRequest);
} else {
// 1. synthèse
final CounterRequest globalRequest = counterRequestAggregation.getGlobalRequest();
final List<CounterRequest> summaryRequests = Arrays.asList(globalRequest,
counterRequestAggregation.getWarningRequest(),
counterRequestAggregation.getSevereRequest());
writeRequests(counter.getChildCounterName(), summaryRequests);
}
}
String getCounterName() {
return counter.getName();
}
String getCounterIconName() {
return counter.getIconName();
}
boolean isErrorCounter() {
return counter.isErrorCounter();
}
private boolean isJobCounter() {
return counter.isJobCounter();
}
private boolean isErrorAndNotJobCounter() {
return isErrorCounter() && !isJobCounter();
}
void writeRequestDetails() throws DocumentException, IOException {
// détails des requêtes
final List<CounterRequest> requests = counterRequestAggregation.getRequests();
if (requests.isEmpty()) {
writeNoRequests();
} else {
// on n'inclue pas pour l'instant les graphs d'évolution des requêtes
// pour des raisons de place et de volume
writeRequests(counter.getChildCounterName(), requests);
}
}
private void writeNoRequests() throws DocumentException {
final String msg;
if (isJobCounter()) {
msg = "Aucun_job";
} else if (isErrorCounter()) {
msg = "Aucune_erreur";
} else {
msg = "Aucune_requete";
}
addToDocument(new Phrase(getString(msg), normalFont));
}
void writeErrorDetails() throws DocumentException {
// détails des erreurs
new PdfCounterErrorReport(counter, getDocument()).toPdf();
}
void writeRequests(String childCounterName, List<CounterRequest> requestList)
throws DocumentException, IOException {
assert requestList != null;
writeHeader(childCounterName);
for (final CounterRequest request : requestList) {
nextRow();
writeRequest(request);
}
addTableToDocument();
// débit et liens
writeFooter();
}
private void writeHeader(String childCounterName) throws DocumentException {
final List<String> headers = createHeaders(childCounterName);
final int[] relativeWidths = new int[headers.size()];
Arrays.fill(relativeWidths, 0, headers.size(), 1);
relativeWidths[0] = 10; // requête
if (includeGraph) {
relativeWidths[0] = 8;
relativeWidths[1] = 2; // graph d'évolution
}
initTable(headers, relativeWidths);
}
private List<String> createHeaders(String childCounterName) {
final List<String> headers = new ArrayList<String>();
if (isJobCounter()) {
headers.add(getString("Job"));
} else if (isErrorCounter()) {
headers.add(getString("Erreur"));
} else {
headers.add(getString("Requete"));
}
if (includeGraph) {
headers.add(getString("Evolution"));
}
if (counterRequestAggregation.isTimesDisplayed()) {
headers.add(getString("temps_cumule"));
headers.add(getString("Hits"));
headers.add(getString("Temps_moyen"));
headers.add(getString("Temps_max"));
headers.add(getString("Ecart_type"));
} else {
headers.add(getString("Hits"));
}
if (counterRequestAggregation.isCpuTimesDisplayed()) {
headers.add(getString("temps_cpu_cumule"));
headers.add(getString("Temps_cpu_moyen"));
}
if (!isErrorAndNotJobCounter()) {
headers.add(getString("erreur_systeme"));
}
if (counterRequestAggregation.isResponseSizeDisplayed()) {
headers.add(getString("Taille_moyenne"));
}
if (counterRequestAggregation.isChildHitsDisplayed()) {
headers.add(getFormattedString("hits_fils_moyens", childCounterName));
headers.add(getFormattedString("temps_fils_moyen", childCounterName));
}
return headers;
}
private void writeFooter() throws DocumentException {
final List<CounterRequest> requests = counterRequestAggregation.getRequests();
final CounterRequest globalRequest = counterRequestAggregation.getGlobalRequest();
// delta ni négatif ni à 0
final long deltaMillis = Math
.max(System.currentTimeMillis() - counter.getStartDate().getTime(), 1);
final long hitsParMinute = 60 * 1000 * globalRequest.getHits() / deltaMillis;
final String key;
if (isJobCounter()) {
key = "nb_jobs";
} else if (isErrorCounter()) {
key = "nb_erreurs";
} else {
key = "nb_requetes";
}
final Paragraph footer = new Paragraph(getFormattedString(key,
integerFormat.format(hitsParMinute), integerFormat.format(requests.size())),
normalFont);
footer.setAlignment(Element.ALIGN_RIGHT);
addToDocument(footer);
}
private void writeRequest(CounterRequest request) throws BadElementException, IOException {
getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT);
final String name = request.getName();
if (name.length() > 1000) {
// si la requête fait plus de 1000 caractères, on la coupe pour y voir quelque chose
addCell(name.substring(0, 1000) + "...");
} else {
addCell(name);
}
if (includeGraph) {
writeRequestGraph(request);
}
getDefaultCell().setHorizontalAlignment(Element.ALIGN_RIGHT);
final CounterRequest globalRequest = counterRequestAggregation.getGlobalRequest();
if (counterRequestAggregation.isTimesDisplayed()) {
addPercentageCell(request.getDurationsSum(), globalRequest.getDurationsSum());
addCell(integerFormat.format(request.getHits()));
final int mean = request.getMean();
addCell(new Phrase(integerFormat.format(mean), getSlaFont(mean)));
addCell(integerFormat.format(request.getMaximum()));
addCell(integerFormat.format(request.getStandardDeviation()));
} else {
addCell(integerFormat.format(request.getHits()));
}
if (counterRequestAggregation.isCpuTimesDisplayed()) {
addPercentageCell(request.getCpuTimeSum(), globalRequest.getCpuTimeSum());
final int cpuTimeMean = request.getCpuTimeMean();
addCell(new Phrase(integerFormat.format(cpuTimeMean), getSlaFont(cpuTimeMean)));
}
if (!isErrorAndNotJobCounter()) {
addCell(systemErrorFormat.format(request.getSystemErrorPercentage()));
}
if (counterRequestAggregation.isResponseSizeDisplayed()) {
addCell(integerFormat.format(request.getResponseSizeMean() / 1024));
}
if (counterRequestAggregation.isChildHitsDisplayed()) {
addCell(integerFormat.format(request.getChildHitsMean()));
addCell(integerFormat.format(request.getChildDurationsMean()));
}
}
private void writeRequestGraph(CounterRequest request) throws BadElementException, IOException {
final JRobin jrobin = collector.getJRobin(request.getId());
if (jrobin == null) {
addCell("");
} else {
final byte[] img = jrobin.graph(range, 100, 50);
final Image image = Image.getInstance(img);
image.scalePercent(50);
addCell(image);
}
}
Font getSlaFont(int mean) {
final Font font;
if (mean < counterRequestAggregation.getWarningThreshold() || mean == 0) {
// si cette moyenne est < à la moyenne globale + 1 écart-type (paramétrable), c'est bien
font = infoCellFont;
} else if (mean < counterRequestAggregation.getSevereThreshold()) {
// sinon, si cette moyenne est < à la moyenne globale + 2 écart-types (paramétrable),
// attention à cette requête qui est plus longue que les autres
font = warningCellFont;
} else {
// sinon, (cette moyenne est > à la moyenne globale + 2 écart-types),
// cette requête est très longue par rapport aux autres ;
// il peut être opportun de l'optimiser si possible
font = severeCellFont;
}
return font;
}
private void addPercentageCell(long dividende, long diviseur) {
if (diviseur == 0) {
addCell("0");
} else {
addCell(integerFormat.format(100 * dividende / diviseur));
}
}
}