Express (Distributed operating systems), v8.0 > Troubleshoot and support > Analyzing application server Java system dumps with the IBM Monitoring and Diagnostic Tools for Java - Dump Analyzer > Write Dump Analyzer modules for WAS diagnostics - Tutorial
Tutorial: Writing Dump Analyzer modules for WAS diagnostics
Generate Reports with a Summary and a Details Section
So far, our analyzer generates a large monolithic report: it scans the dump for all the information that we want to extract, and prints it in the report essentially in the order that it is found. This is obviously the simplest approach from the perspective of the people implementing the analyzer. But from the perspective of people who will have to read the report generated by this analyzer, we may want to provide a more user-friendly structure, for example by showing a summary of the most important information near the top of the report, followed by more details further down in the report. We can do this by taking advantage of the ability to nest multiple reports into another higher-level report.
For this sample, let's show a summary section with some global statistics about all thread pools and a short list of of threads pools (one line per pool), followed by a detailed section that shows all the information about each pool.
Here is a synopsis of the new code:
public class WASThreadPoolsSampleX extends WASAnalyzerBase implements IReport {public IAnalysisReport produceReport() { IAnalysisReport mainReport = allocateReport(null); IAnalysisReport summaryStatsReport = allocateSecondaryReport("summaryStats", null); IAnalysisReport summaryListReport = allocateSecondaryReport("summaryList", null); IAnalysisReport detailsReport = allocateSecondaryReport("details", null);
/* * Scan all the pools to collect both summary and detailed information */ scanAllPools(summaryStatsReport, summaryListReport, detailsReport);
/* * Print the summary information */ mainReport.startSection("Summary information", IAnalysisReport.TAG_MAJORHEADING); mainReport.printReport("Global Statistics", summaryStatsReport); mainReport.printReport("Summary list of pools", summaryListReport); mainReport.endSection();
/* * Print the detailed information */ mainReport.printReport("Detailed information", detailsReport, IAnalysisReport.TAG_MAJORHEADING);
return mainReport; }
private void scanAllPools(IAnalysisReport summaryStatsReport, IAnalysisReport summaryListReport, IAnalysisReport detailsReport) { ... }
}
Basically, instead of writing all the output directly into a single report, we now allocate and use 4 distinct reports:
- summaryStatsReport is a secondary report where we will construct the output related to the global statistics collected by this analyzer: total number of pools, total number of threads, etc.
- summaryListReport is a secondary report where we will construct a list of all the pools, one line per pool
- detailsReport is a secondary report where we will write the complete details about every pool, one after the other, as we used to do in the single main report in the previous steps of this tutorial
- mainReport is the top-level report returned by this analyzer, in which we will insert the other three reports, along with any other information that we wish to include
The main produceReport() method allocates these 4 report objects, invokes scanAllPools() to do all the work to extract information about all the pools and fill-in the information in the 3 secondary reports, and then constructs the main report by using IAnalysisReport.printReport() to include each secondary report in the main report.
Let's look at the implementation of scanAllPools():
... /* * Some state being built as part of the analysis */ private int numberOfPools = 0; private int numberOfAllocatedThreads = 0; private int numberOfPossibleThreads = 0; private int numberOfErrors = 0;private void scanAllPools(IAnalysisReport summaryStatsReport, IAnalysisReport summaryListReport, IAnalysisReport detailsReport) { /* * Iterate over all the pools, computing the global statistics and bulding a list */ ObjectWrapperCollection pools = ObjectWrapperCollection.getObjectInstances(getContext(), "com/ibm/ws/util/ThreadPool"); for (int index = 0; index < pools.size(); index++) { ObjectWrapper pool = (ObjectWrapper) pools.get(index); scanOnePool(pool, summaryListReport, detailsReport); }
/* * Generate the global statistics report */ summaryStatsReport.printField("Number of pools successfully scanned", numberOfPools); summaryStatsReport.printField("Number of pools scanned with errors", numberOfErrors); summaryStatsReport.printField("Current number of pool threads", numberOfAllocatedThreads); summaryStatsReport.printField("Maximum possible number of pool threads", numberOfPossibleThreads); // ... other statistics ... }
private void scanOnePool(ObjectWrapper pool, IAnalysisReport summaryListReport, IAnalysisReport detailsReport) { // ... see code from previous sections ... }
We first declare a few object instance fields numberOfPools, numberOfAllocatedThreads(), etc. to gather statistics. scanAllPools() and ScanOnePool() will increment these statistics as they iterate through all the pools.
scanAllPools() first iterates over each pool, and calls scanOnePool() once for each pool. scanOnePool() contains code similar to that from previous sections in this tutorials. It appends information to both the summaryListReport() and the detailsReport, in parallel.
Finally, scanAllPools() fills the the summaryStartReport() with the global statistics that have been collected during the entire scan.
Next Topic
Integrate the Analyzer into the Tool Menu