Skip to content

Commit

Permalink
in the detail of a request, add pdf report
Browse files Browse the repository at this point in the history
  • Loading branch information
evernat committed Feb 1, 2017
1 parent ec2638e commit 28e2474
Show file tree
Hide file tree
Showing 4 changed files with 331 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -845,9 +845,14 @@ void writeRefreshAndPeriodLinks(String graphName, String part) throws IOExceptio
+ "' title='#Rafraichir#'>");
}
write("<img src='?resource=action_refresh.png' alt='#Actualiser#'/> #Actualiser#</a>");
if (graphName == null && isPdfEnabled()) {
if (isPdfEnabled()) {
writeln(separator);
write("<a href='?format=pdf' title='#afficher_PDF#'>");
if (graphName == null) {
write("<a href='?format=pdf' title='#afficher_PDF#'>");
} else {
write("<a href='?part=" + part + graphParameter + urlEncode(graphName)
+ "&amp;format=pdf' title='#afficher_PDF#'>");
}
write("<img src='?resource=pdf.png' alt='#PDF#'/> #PDF#</a>");
}
writeln(separator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import static net.bull.javamelody.HttpParameters.CURRENT_REQUESTS_PART;
import static net.bull.javamelody.HttpParameters.DATABASE_PART;
import static net.bull.javamelody.HttpParameters.GRAPH_PARAMETER;
import static net.bull.javamelody.HttpParameters.GRAPH_PART;
import static net.bull.javamelody.HttpParameters.HEAP_HISTO_PART;
import static net.bull.javamelody.HttpParameters.HOTSPOTS_PART;
import static net.bull.javamelody.HttpParameters.JNDI_PART;
Expand Down Expand Up @@ -117,6 +118,12 @@ private void doPdfPart(HttpServletRequest httpRequest, HttpServletResponse httpR
final PdfOtherReport pdfOtherReport = new PdfOtherReport(getApplication(),
httpResponse.getOutputStream());
pdfOtherReport.writeCounterSummaryPerClass(collector, counter, requestId, range);
} else if (GRAPH_PART.equalsIgnoreCase(part)) {
final String requestId = httpRequest.getParameter(GRAPH_PARAMETER);
final Range range = httpCookieManager.getRange(httpRequest, httpResponse);
final PdfOtherReport pdfOtherReport = new PdfOtherReport(getApplication(),
httpResponse.getOutputStream());
pdfOtherReport.writeRequestAndGraphDetail(collector, collectorServer, range, requestId);
} else {
throw new IllegalArgumentException(part);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,18 @@ void writeAllCurrentRequestsAsPart(
document.close();
}

void writeRequestAndGraphDetail(Collector collector, CollectorServer collectorServer,
Range range, String requestId) throws IOException {
try {
document.open();
new PdfRequestAndGraphDetailReport(collector, collectorServer, range, requestId,
pdfDocumentFactory, document).toPdf();
} catch (final DocumentException e) {
throw createIOException(e);
}
document.close();
}

void writeRuntimeDependencies(Counter counter, Range range) throws IOException {
try {
final Document myDocument = pdfDocumentFactory.createDocument(true);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
/*
* Copyright 2008-2016 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.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import com.lowagie.text.Chunk;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.FontFactory;
import com.lowagie.text.Image;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;

/**
* Rapport pdf pour le détail d'une requête.
* @author Emeric Vernat
*/
public class PdfRequestAndGraphDetailReport extends PdfAbstractTableReport {
private final Collector collector;
private final CollectorServer collectorServer;
private final Range range;
private final List<Counter> counters;
private final String graphName;
private final CounterRequest request;
private final Map<String, CounterRequest> requestsById;
private final PdfDocumentFactory pdfDocumentFactory;
private final DecimalFormat systemErrorFormat = I18N.createPercentFormat();
private final DecimalFormat nbExecutionsFormat = I18N.createPercentFormat();
private final DecimalFormat integerFormat = I18N.createIntegerFormat();
private final Font cellFont = PdfFonts.TABLE_CELL.getFont();
private final Font boldFont = PdfFonts.BOLD.getFont();
private final Font courierFont = FontFactory.getFont(FontFactory.COURIER, 5.5f, Font.NORMAL);

PdfRequestAndGraphDetailReport(Collector collector, CollectorServer collectorServer,
Range range, String graphName, PdfDocumentFactory pdfDocumentFactory, Document document)
throws IOException {
super(document);
assert collector != null;
assert range != null;
assert graphName != null;
assert pdfDocumentFactory != null;
this.collector = collector;
this.collectorServer = collectorServer;
this.range = range;
this.graphName = graphName;
this.counters = collector.getRangeCounters(range);
this.requestsById = mapAllRequestsById();
this.request = requestsById.get(graphName);
this.pdfDocumentFactory = pdfDocumentFactory;
}

private Map<String, CounterRequest> mapAllRequestsById() {
final Map<String, CounterRequest> result = new HashMap<String, CounterRequest>();
for (final Counter counter : counters) {
for (final CounterRequest aRequest : counter.getRequests()) {
result.put(aRequest.getId(), aRequest);
}
}
return result;
}

@Override
void toPdf() throws DocumentException, IOException {
if (request != null) {
writeHeader();

writeRequests();

addTableToDocument();

if (JdbcWrapper.SINGLETON.getSqlCounter().isRequestIdFromThisCounter(request.getId())
&& !request.getName().toLowerCase(Locale.ENGLISH).startsWith("alter ")) {
// inutile d'essayer d'avoir le plan d'exécution des requêtes sql
// telles que "alter session set ..." (cf issue 152)
writeSqlRequestExplainPlan();
}
}

if (isGraphDisplayed()) {
writeGraph();
}

if (request != null && request.getStackTrace() != null) {
final Paragraph paragraph = new Paragraph("\n", cellFont);
paragraph.setIndentationLeft(20);
paragraph.setIndentationRight(20);
paragraph.add(new Phrase("Stack-trace\n", boldFont));
paragraph.add(new Phrase(request.getStackTrace().replace("\t", " "), cellFont));
addToDocument(paragraph);
}
}

private void writeHeader() throws DocumentException {
final List<String> headers = createHeaders();
final int[] relativeWidths = new int[headers.size()];
Arrays.fill(relativeWidths, 0, headers.size(), 1);
relativeWidths[0] = 8; // requête

initTable(headers, relativeWidths);
}

private List<String> createHeaders() {
final List<String> headers = new ArrayList<String>();
headers.add(getString("Requete"));
final boolean hasChildren = !request.getChildRequestsExecutionsByRequestId().isEmpty();
if (hasChildren) {
headers.add(getString("Hits_par_requete"));
}
headers.add(getString("Temps_moyen"));
headers.add(getString("Temps_max"));
headers.add(getString("Ecart_type"));
headers.add(getString("Temps_cpu_moyen"));
headers.add(getString("erreur_systeme"));
final Counter parentCounter = getCounterByRequestId(request);
final boolean allChildHitsDisplayed = parentCounter != null
&& parentCounter.getChildCounterName() != null && request.hasChildHits();
if (allChildHitsDisplayed) {
final String childCounterName = parentCounter.getChildCounterName();
headers.add(getFormattedString("hits_fils_moyens", childCounterName));
headers.add(getFormattedString("temps_fils_moyen", childCounterName));
}
return headers;
}

private void writeRequests() throws IOException, DocumentException {
final Map<String, Long> childRequests = request.getChildRequestsExecutionsByRequestId();
final boolean hasChildren = !childRequests.isEmpty();
final Counter parentCounter = getCounterByRequestId(request);
final boolean allChildHitsDisplayed = parentCounter != null
&& parentCounter.getChildCounterName() != null && request.hasChildHits();

final PdfPCell defaultCell = getDefaultCell();
defaultCell.setLeading(2, 1);
defaultCell.setPaddingTop(0);

nextRow();
writeRequest(request, -1, allChildHitsDisplayed);

if (hasChildren) {
writeChildRequests(childRequests, allChildHitsDisplayed);
}
}

private void writeChildRequests(Map<String, Long> childRequests, boolean allChildHitsDisplayed)
throws IOException, DocumentException {
for (final Map.Entry<String, Long> entry : childRequests.entrySet()) {
final CounterRequest childRequest = requestsById.get(entry.getKey());
if (childRequest != null) {
nextRow();
final Long nbExecutions = entry.getValue();
final float executionsByRequest = (float) nbExecutions / request.getHits();
writeRequest(childRequest, executionsByRequest, allChildHitsDisplayed);
}
}
}

private void writeRequest(CounterRequest childRequest, float executionsByRequest,
boolean allChildHitsDisplayed) throws IOException, DocumentException {
final PdfPCell defaultCell = getDefaultCell();
defaultCell.setHorizontalAlignment(Element.ALIGN_LEFT);
final Paragraph paragraph = new Paragraph(defaultCell.getLeading() + cellFont.getSize());
if (executionsByRequest != -1) {
paragraph.setIndentationLeft(5);
}
final Counter parentCounter = getCounterByRequestId(childRequest);
if (parentCounter != null && parentCounter.getIconName() != null) {
paragraph.add(new Chunk(getSmallImage(parentCounter.getIconName()), 0, -1));
}
paragraph.add(new Phrase(childRequest.getName(), cellFont));
final PdfPCell requestCell = new PdfPCell();
requestCell.addElement(paragraph);
requestCell.setGrayFill(defaultCell.getGrayFill());
requestCell.setPaddingTop(defaultCell.getPaddingTop());
addCell(requestCell);

defaultCell.setHorizontalAlignment(Element.ALIGN_RIGHT);
if (executionsByRequest != -1) {
addCell(nbExecutionsFormat.format(executionsByRequest));
} else {
addCell("");
}
writeRequestValues(childRequest, allChildHitsDisplayed);
}

private void writeRequestValues(CounterRequest aRequest, boolean allChildHitsDisplayed) {
final PdfPCell defaultCell = getDefaultCell();
defaultCell.setHorizontalAlignment(Element.ALIGN_RIGHT);
addCell(integerFormat.format(aRequest.getMean()));
addCell(integerFormat.format(aRequest.getMaximum()));
addCell(integerFormat.format(aRequest.getStandardDeviation()));
if (aRequest.getCpuTimeMean() >= 0) {
addCell(integerFormat.format(aRequest.getCpuTimeMean()));
} else {
addCell("");
}
addCell(systemErrorFormat.format(aRequest.getSystemErrorPercentage()));
if (allChildHitsDisplayed) {
final boolean childHitsDisplayed = aRequest.hasChildHits();
if (childHitsDisplayed) {
addCell(integerFormat.format(aRequest.getChildHitsMean()));
} else {
addCell("");
}
if (childHitsDisplayed) {
addCell(integerFormat.format(aRequest.getChildDurationsMean()));
} else {
addCell("");
}
}
}

private void writeGraph() throws IOException, DocumentException {
final JRobin jrobin = collector.getJRobin(graphName);
if (jrobin != null) {
final byte[] img = jrobin.graph(range, 960, 400);
final Image image = Image.getInstance(img);
image.scalePercent(50);

final PdfPTable table = new PdfPTable(1);
table.setHorizontalAlignment(Element.ALIGN_CENTER);
table.setWidthPercentage(100);
table.getDefaultCell().setBorder(0);
table.addCell("\n");
table.addCell(image);
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_RIGHT);
table.addCell(new Phrase(getString("graph_units"), cellFont));
addToDocument(table);
} else {
// just in case request is null and collector.getJRobin(graphName) is null, we must write something in the document
addToDocument(new Phrase("\n", cellFont));
}
}

private void writeSqlRequestExplainPlan() throws DocumentException {
try {
final String explainPlan;
if (collectorServer == null) {
explainPlan = DatabaseInformations.explainPlanFor(request.getName());
} else {
explainPlan = collectorServer.collectSqlRequestExplainPlan(
collector.getApplication(), request.getName());
}
if (explainPlan != null) {
final Paragraph paragraph = new Paragraph("", cellFont);
paragraph.add(new Phrase('\n' + getString("Plan_d_execution") + '\n', boldFont));
paragraph.add(new Phrase(explainPlan, courierFont));
addToDocument(paragraph);
}
} catch (final Exception e) {
final Paragraph paragraph = new Paragraph("", cellFont);
paragraph.add(new Phrase('\n' + getString("Plan_d_execution") + '\n', boldFont));
paragraph.add(new Phrase(e.toString(), cellFont));
addToDocument(paragraph);
}
}

private Counter getCounterByRequestId(CounterRequest aRequest) {
final String myRequestId = aRequest.getId();
for (final Counter counter : counters) {
if (counter.isRequestIdFromThisCounter(myRequestId)) {
return counter;
}
}
return null;
}

private boolean isGraphDisplayed() {
return request == null || getCounterByRequestId(request) != null
&& HtmlCounterReport.isRequestGraphDisplayed(getCounterByRequestId(request))
// on vérifie aussi que l'instance de jrobin existe pour faire le graph,
// notamment si les statistiques ont été réinitialisées, ce qui vide les instances de jrobin
&& collector.getJRobin(request.getId()) != null;
}

private Image getSmallImage(String resourceFileName) throws DocumentException, IOException {
return pdfDocumentFactory.getSmallImage(resourceFileName);
}
}

0 comments on commit 28e2474

Please sign in to comment.