Skip to content

Feature/#61: Support GC log file rotation #176

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Aug 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ is free software released under GNU LGPL.
You can start GCViewer (gui) by simply double-clicking on gcviewer-1.3x.jar
or running java -jar gcviewer-1.3x.jar (it needs a java 1.8 vm to run).

For a cmdline based report summary just type:
java -jar gcviewer-1.3x.jar gc.log summary.csv [chart.png] [-t PLAIN|CSV|CSV_TS|SIMPLE|SUMMARY]

to generate a report (including optional chart image file).
For a cmdline based report summary just type the following to generate a report (including optional chart image file):
`java -jar gcviewer-1.3x.jar gc.log summary.csv [chart.png] [-t PLAIN|CSV|CSV_TS|SIMPLE|SUMMARY]`
When logfile rotation (-XX:+UseGCLogFileRotation) is enabled, the logfiles can be read at once:
`java -jar gcviewer-1.3x.jar gc.log.0;gc.log.1;gc.log.2;gc.log.current summary.csv [chart.png] [-t PLAIN|CSV|CSV_TS|SIMPLE|SUMMARY]`


Supported verbose:gc formats are:
Expand Down
54 changes: 34 additions & 20 deletions src/main/java/com/tagtraum/perf/gcviewer/GCViewer.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
package com.tagtraum.perf.gcviewer;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.tagtraum.perf.gcviewer.ctrl.impl.GCViewerGuiController;
import com.tagtraum.perf.gcviewer.exp.DataWriter;
import com.tagtraum.perf.gcviewer.exp.DataWriterType;
Expand All @@ -17,6 +10,13 @@
import com.tagtraum.perf.gcviewer.model.GCResource;
import com.tagtraum.perf.gcviewer.view.SimpleChartRenderer;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Main class of GCViewer. Parses command line parameters if there are any and either remains
* in command line mode or starts the gui (depending on parameters).
Expand All @@ -26,13 +26,24 @@ public class GCViewer {
private static final int EXIT_OK = 0;
private static final int EXIT_EXPORT_FAILED = -1;
private static final int EXIT_ARGS_PARSE_FAILED = -2;
private GCViewerGuiController gcViewerGuiController;
private GCViewerArgsParser gcViewerArgsParser;

public GCViewer() {
this(new GCViewerGuiController(), new GCViewerArgsParser());
}

public GCViewer(GCViewerGuiController gcViewerGuiController, GCViewerArgsParser gcViewerArgsParser) {
this.gcViewerGuiController = gcViewerGuiController;
this.gcViewerArgsParser = gcViewerArgsParser;
}

public static void main(final String[] args) throws InvocationTargetException, InterruptedException {
public static void main(final String[] args) throws InvocationTargetException, InterruptedException {
new GCViewer().doMain(args);
}

public void doMain(String[] args) throws InvocationTargetException, InterruptedException {
GCViewerArgsParser argsParser = new GCViewerArgsParser();
GCViewerArgsParser argsParser = gcViewerArgsParser;
try {
argsParser.parseArguments(args);
}
Expand All @@ -47,14 +58,14 @@ public void doMain(String[] args) throws InvocationTargetException, InterruptedE
}
else if (argsParser.getArgumentCount() >= 2) {
LOGGER.info("GCViewer command line mode");
String gcfile = argsParser.getGcfile();
GCResource gcResource = argsParser.getGcResource();
String summaryFilePath = argsParser.getSummaryFilePath();
String chartFilePath = argsParser.getChartFilePath();
DataWriterType type = argsParser.getType();

//export summary:
try {
export(gcfile, summaryFilePath, chartFilePath, type);
export(gcResource, summaryFilePath, chartFilePath, type);
LOGGER.info("export completed successfully");
System.exit(EXIT_OK);
}
Expand All @@ -64,22 +75,22 @@ else if (argsParser.getArgumentCount() >= 2) {
}
}
else {
new GCViewerGuiController().startGui(argsParser.getArgumentCount() == 1 ? argsParser.getGcfile() : null);
gcViewerGuiController.startGui(argsParser.getArgumentCount() == 1 ? argsParser.getGcResource() : null);
}
}

private void export(String gcFilename, String summaryFilePath, String chartFilePath, DataWriterType type)
private void export(GCResource gcResource, String summaryFilePath, String chartFilePath, DataWriterType type)
throws IOException, DataReaderException {

DataReaderFacade dataReaderFacade = new DataReaderFacade();
GCModel model = dataReaderFacade.loadModel(new GCResource(gcFilename));
GCModel model = dataReaderFacade.loadModel(gcResource);

exportType(model, summaryFilePath, type);
if (chartFilePath != null)
renderChart(model, chartFilePath);
}

private void exportType(GCModel model, String summaryFilePath, DataWriterType type) throws IOException {
private void exportType(GCModel model, String summaryFilePath, DataWriterType type) throws IOException {
try (DataWriter summaryWriter = DataWriterFactory.getDataWriter(new File(summaryFilePath), type)) {
summaryWriter.write(model);
}
Expand All @@ -90,13 +101,16 @@ private void renderChart(GCModel model, String chartFilePath) throws IOException
renderer.render(model, new FileOutputStream(new File(chartFilePath)));
}

private static void usage() {
System.out.println("Welcome to GCViewer with cmdline");
private static void usage() {
System.out.println("Welcome to GCViewer with cmdline");
System.out.println("java -jar gcviewer.jar [<gc-log-file|url>] -> opens gui and loads given file");
System.out.println("java -jar gcviewer.jar [<gc-log-file|url>];[<gc-log-file|url>];[...] -> opens gui and loads given files as series of rotated logfiles");
System.out.println("java -jar gcviewer.jar [<gc-log-file>] [<export.csv>] -> cmdline: writes report to <export.csv>");
System.out.println("java -jar gcviewer.jar [<gc-log-file>] [<export.csv>] [<chart.png>] " +
"-> cmdline: writes report to <export.csv> and renders gc chart to <chart.png>");
System.out.println("java -jar gcviewer.jar [<gc-log-file>] [<export.csv>] [<chart.png>] [-t <SUMMARY, CSV, CSV_TS, PLAIN, SIMPLE>]");
System.out.println("java -jar gcviewer.jar [<gc-log-file|url>];[<gc-log-file|url>];[...] [<export.csv>] -> cmdline: loads given files as series of rotated logfiles and writes report to <export.csv>");
System.out.println("java -jar gcviewer.jar [<gc-log-file>] [<export.csv>] [<chart.png>] -> cmdline: writes report to <export.csv> and renders gc chart to <chart.png>");
System.out.println("java -jar gcviewer.jar [<gc-log-file|url>];[<gc-log-file|url>];[...] [<export.csv>] [<chart.png>] -> cmdline: loads given files as series of rotated logfiles and writes report to <export.csv> and renders gc chart to <chart.png>");
System.out.println("java -jar gcviewer.jar [<gc-log-file|url>] [<export.csv>] [<chart.png>] [-t <SUMMARY, CSV, CSV_TS, PLAIN, SIMPLE>]");
System.out.println("java -jar gcviewer.jar [<gc-log-file|url>];[<gc-log-file|url>];[...] [<export.csv>] [<chart.png>] [-t <SUMMARY, CSV, CSV_TS, PLAIN, SIMPLE>]");
}

}
28 changes: 21 additions & 7 deletions src/main/java/com/tagtraum/perf/gcviewer/GCViewerArgsParser.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package com.tagtraum.perf.gcviewer;

import com.tagtraum.perf.gcviewer.exp.DataWriterType;
import com.tagtraum.perf.gcviewer.model.GCResource;
import com.tagtraum.perf.gcviewer.model.GcResourceFile;
import com.tagtraum.perf.gcviewer.model.GcResourceSeries;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.tagtraum.perf.gcviewer.exp.DataWriterType;
import java.util.stream.Collectors;

/**
* Parser for commandline arguments.
Expand All @@ -18,7 +22,7 @@ public class GCViewerArgsParser {

private int argumentCount;
private String chartFilePath;
private String gcfile;
private String gcFile;
private String summaryFilePath;
private DataWriterType type = DataWriterType.SUMMARY;

Expand All @@ -29,9 +33,19 @@ public int getArgumentCount() {
public String getChartFilePath() {
return chartFilePath;
}

public String getGcfile() {
return gcfile;

public GCResource getGcResource() {
List<String> files = Arrays.asList(gcFile.split(";"));
List<GCResource> resources = files.stream().map(GcResourceFile::new).collect(Collectors.toList());
if (resources.isEmpty())
throw new IllegalStateException("Found no valid resource!");

if (resources.size() == 1) {
return resources.get(0);
}
else {
return new GcResourceSeries(resources);
}
}

public String getSummaryFilePath() {
Expand Down Expand Up @@ -65,7 +79,7 @@ else if (typeIdx != -1) {
}

argumentCount = argsList.size();
gcfile = safeGetArgument(argsList, ARG_POS_GCFILE);
gcFile = safeGetArgument(argsList, ARG_POS_GCFILE);
summaryFilePath = safeGetArgument(argsList, ARG_POS_SUMMARY_FILE);
chartFilePath = safeGetArgument(argsList, ARG_POS_CHART_FILE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ public interface GCModelLoaderController {

void open(List<GCResource> gcResourceList);

/**
* Opens the given {@link GCResource}s as a series of rotated logfiles.
*
* @param gcResourceList a list of rotated gc logfiles. Ordering is not required.
*/
void openAsSeries(List<GCResource> gcResourceList);

/**
* Reload all models of <code>gcDocument</code> and provide tracker. The tracker will
* fire a propertyChangeEvent, as soon as all GCModelLoaders have finished loading.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.tagtraum.perf.gcviewer.ctrl.action;

import com.tagtraum.perf.gcviewer.ctrl.GCModelLoaderController;
import com.tagtraum.perf.gcviewer.model.GCResource;
import com.tagtraum.perf.gcviewer.model.GcResourceFile;
import com.tagtraum.perf.gcviewer.util.LocalisationHelper;
import com.tagtraum.perf.gcviewer.view.ActionCommands;
import com.tagtraum.perf.gcviewer.view.GCViewerGui;
import com.tagtraum.perf.gcviewer.view.OpenFileView;
import com.tagtraum.perf.gcviewer.view.util.ImageHelper;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

/**
* Allows to open a series of log files, treating them as a consecutive log.
*
* @author martin.geldmacher
*/
public class OpenSeries extends AbstractAction {
private static final Logger logger = Logger.getLogger(OpenSeries.class.getName());

private GCModelLoaderController controller;
private GCViewerGui gcViewer;
private OpenFileView openFileView;

public OpenSeries(GCModelLoaderController controller, final GCViewerGui gcViewer) {
this.controller = controller;
this.gcViewer = gcViewer;

putValue(NAME, LocalisationHelper.getString("main_frame_menuitem_open_series"));
putValue(SHORT_DESCRIPTION, LocalisationHelper.getString("main_frame_menuitem_hint_open_series"));
putValue(MNEMONIC_KEY, Integer.valueOf(LocalisationHelper.getString("main_frame_menuitem_mnemonic_open_series").charAt(0)));
putValue(ACTION_COMMAND_KEY, ActionCommands.OPEN_SERIES.toString());
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke('S', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
putValue(SMALL_ICON, ImageHelper.loadImageIcon("open.png"));

openFileView = new OpenFileView();
}

@Override
public void actionPerformed(ActionEvent e) {
final int val = openFileView.showOpenDialog(gcViewer);
if (val == JFileChooser.APPROVE_OPTION) {
File[] selectedFiles = openFileView.getSelectedFiles();
List<GCResource> resources = getResources(selectedFiles);
controller.openAsSeries(resources);
}
}

private List<GCResource> getResources(File[] selectedFiles) {
if (selectedFiles == null || selectedFiles.length == 0)
throw new IllegalArgumentException("At least one file must be selected!");

java.util.List<GCResource> resources = new ArrayList<>();
for (File file : selectedFiles) {
resources.add(new GcResourceFile(file));
}
return resources;
}
}
17 changes: 7 additions & 10 deletions src/main/java/com/tagtraum/perf/gcviewer/ctrl/action/OpenURL.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
package com.tagtraum.perf.gcviewer.ctrl.action;

import java.awt.Toolkit;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.KeyStroke;

import com.tagtraum.perf.gcviewer.ctrl.GCModelLoaderController;
import com.tagtraum.perf.gcviewer.model.GCResource;
import com.tagtraum.perf.gcviewer.model.GcResourceFile;
import com.tagtraum.perf.gcviewer.util.LocalisationHelper;
import com.tagtraum.perf.gcviewer.view.ActionCommands;
import com.tagtraum.perf.gcviewer.view.OpenUrlView;
import com.tagtraum.perf.gcviewer.view.model.RecentGCResourcesModel;
import com.tagtraum.perf.gcviewer.view.util.ImageHelper;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

/**
* @author <a href="mailto:[email protected]">Hendrik Schreiber</a>
* Date: May 20, 2005
Expand Down Expand Up @@ -43,10 +40,10 @@ public void setRecentResourceNamesModel(RecentGCResourcesModel recentResourceNam
public void actionPerformed(ActionEvent e) {
if (view.showDialog()) {
if (view.isAddCheckBoxSelected()) {
controller.add(new GCResource(view.getSelectedItem()));
controller.add(new GcResourceFile(view.getSelectedItem()));
}
else {
controller.open(new GCResource(view.getSelectedItem()));
controller.open(new GcResourceFile(view.getSelectedItem()));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.tagtraum.perf.gcviewer.ctrl.impl;

import com.tagtraum.perf.gcviewer.ctrl.GCModelLoader;
import com.tagtraum.perf.gcviewer.imp.DataReaderException;
import com.tagtraum.perf.gcviewer.imp.MonitoredBufferedInputStream;
import com.tagtraum.perf.gcviewer.model.GCModel;

import javax.swing.*;
import java.beans.PropertyChangeEvent;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Base class for {@link GCModelLoader}s
*
* @author martin.geldmacher (refactored)
*/
public abstract class AbstractGCModelLoaderImpl extends SwingWorker<GCModel, Object> implements GCModelLoader {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName() == MonitoredBufferedInputStream.PROGRESS) {
setProgress((int) evt.getNewValue());
}
}

protected void done() {
Logger logger = getGcResource().getLogger();

try {
getGcResource().setModel(get());
// TODO delete
getGcResource().getModel().printDetailedInformation();
}
catch (InterruptedException e) {
logger.log(Level.FINE, "model get() interrupted", e);
}
catch (ExecutionException | RuntimeException e) {
if (logger.isLoggable(Level.WARNING))
logger.log(Level.WARNING, "Failed to create GCModel from " + getGcResource().getResourceName(), e);
}
}

@Override
protected GCModel doInBackground() throws Exception {
setProgress(0);
final GCModel result;
try {
result = loadGcModel();
}
catch (DataReaderException | RuntimeException e) {
Logger logger = getGcResource().getLogger();
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Failed to load GCModel from " + getGcResource().getResourceName(), e);
}
throw e;
}
return result;
}

protected abstract GCModel loadGcModel() throws DataReaderException;
}
Loading