diff --git a/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java b/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java index c55ffbc5e..8f79d34b5 100755 --- a/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java +++ b/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java @@ -1,7 +1,9 @@ package org.broadinstitute.sting.gatk; +import org.broadinstitute.sting.utils.GATKErrorReport; import org.broadinstitute.sting.utils.cmdLine.*; +import java.io.PrintStream; import java.util.*; /** @@ -38,17 +40,30 @@ public class CommandLineGATK extends CommandLineExecutable { // our argument collection, the collection of command line args we accept @ArgumentCollection - private GATKArgumentCollection argCollection = new GATKArgumentCollection(); + private GATKArgumentCollection argCollection = new GATKArgumentCollection(); /** * Get pleasing info about the GATK. + * * @return A list of Strings that contain pleasant info about the GATK. */ @Override protected ApplicationDetails getApplicationDetails() { - return new ApplicationDetails( createApplicationHeader(), - ApplicationDetails.createDefaultRunningInstructions(getClass()), - getAdditionalHelp() ); + return new ApplicationDetails(createApplicationHeader(), + ApplicationDetails.createDefaultRunningInstructions(getClass()), + getAdditionalHelp()); + } + + /** + * generate an error log, given the stream to write to and the exception that was thrown + * + * @param stream the output stream + * @param e the exception + */ + @Override + public void generateErrorLog(PrintStream stream, Exception e) { + GATKErrorReport report = new GATKErrorReport(e, this.argCollection); + report.reportToStream(stream); } @Override @@ -61,7 +76,9 @@ public class CommandLineGATK extends CommandLineExecutable { return argCollection; } - /** Required main method implementation. */ + /** + * Required main method implementation. + */ public static void main(String[] argv) { try { CommandLineGATK instance = new CommandLineGATK(); @@ -74,9 +91,10 @@ public class CommandLineGATK extends CommandLineExecutable { /** * Creates the a short blurb about the GATK, copyright info, and where to get documentation. + * * @return The application header. */ - private List createApplicationHeader() { + public static List createApplicationHeader() { List header = new ArrayList(); header.add("The Genome Analysis Toolkit (GATK)"); header.add("Copyright (c) 2009 The Broad Institute"); @@ -89,7 +107,8 @@ public class CommandLineGATK extends CommandLineExecutable { /** * Retrieves additional information about GATK walkers. * TODO: This functionality is very similar to that employed by the HelpFormatter. Generalize - * the code in HelpFormatter and supply it as a helper to this method. + * the code in HelpFormatter and supply it as a helper to this method. + * * @return A string summarizing the walkers available in this distribution. */ private String getAdditionalHelp() { @@ -97,34 +116,34 @@ public class CommandLineGATK extends CommandLineExecutable { Set walkerNames = GATKEngine.getWalkerNames(); // Sort the list of walker names. - walkerNames = new TreeSet( walkerNames ); + walkerNames = new TreeSet(walkerNames); // Construct a help string to output available walkers. StringBuilder additionalHelp = new StringBuilder(); - Formatter formatter = new Formatter( additionalHelp ); + Formatter formatter = new Formatter(additionalHelp); - formatter.format( "Available analyses:%n" ); + formatter.format("Available analyses:%n"); // Compute the max size of any walker name int maxNameLength = 0; - for( String walkerName: walkerNames ) { - if( maxNameLength < walkerName.length() ) + for (String walkerName : walkerNames) { + if (maxNameLength < walkerName.length()) maxNameLength = walkerName.length(); } - + final int fieldWidth = maxNameLength + HelpFormatter.FIELD_SEPARATION_WIDTH; - final int walkersPerLine = Math.min(HelpFormatter.LINE_WIDTH / fieldWidth, 4 ); + final int walkersPerLine = Math.min(HelpFormatter.LINE_WIDTH / fieldWidth, 4); final int columnSpacing = Math.max((HelpFormatter.LINE_WIDTH - (fieldWidth * walkersPerLine)) / walkersPerLine, 1); int currentWalkerName = 0; - for( String walkerName: walkerNames ) { - formatter.format( "%-" + HelpFormatter.FIELD_SEPARATION_WIDTH + "s" + - "%-" + fieldWidth + "s" + - "%-" + columnSpacing + "s", "", walkerName, "" ); - if( ++currentWalkerName % walkersPerLine == 0 ) + for (String walkerName : walkerNames) { + formatter.format("%-" + HelpFormatter.FIELD_SEPARATION_WIDTH + "s" + + "%-" + fieldWidth + "s" + + "%-" + columnSpacing + "s", "", walkerName, ""); + if (++currentWalkerName % walkersPerLine == 0) formatter.format("%n"); } - return additionalHelp.toString(); + return additionalHelp.toString(); } } diff --git a/java/src/org/broadinstitute/sting/gatk/GATKArgumentCollection.java b/java/src/org/broadinstitute/sting/gatk/GATKArgumentCollection.java index 7a3c658fc..b399da0a7 100755 --- a/java/src/org/broadinstitute/sting/gatk/GATKArgumentCollection.java +++ b/java/src/org/broadinstitute/sting/gatk/GATKArgumentCollection.java @@ -10,6 +10,7 @@ import org.simpleframework.xml.stream.HyphenStyle; import java.io.File; import java.io.InputStream; +import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -143,7 +144,22 @@ public class GATKArgumentCollection { try { serializer.write(collection, result); } catch (Exception e) { - throw new StingException("Failed to marshal the data from the file " + outputFile, e); + throw new StingException("Failed to marshal the data to the file " + outputFile, e); + } + } + + /** + * marshal the data out to a object + * + * @param collection the GATKArgumentCollection to load into + * @param outputFile the stream to write to + */ + public static void marshal( GATKArgumentCollection collection, PrintStream outputFile ) { + Serializer serializer = new Persister(new Format(new HyphenStyle())); + try { + serializer.write(collection, outputFile); + } catch (Exception e) { + throw new StingException("Failed to marshal the data to the file " + outputFile, e); } } @@ -159,7 +175,7 @@ public class GATKArgumentCollection { GATKArgumentCollection example = serializer.read(GATKArgumentCollection.class, source); return example; } catch (Exception e) { - throw new StingException("Failed to marshal the data to file " + filename, e); + throw new StingException("Failed to marshal the data from file " + filename, e); } } @@ -174,7 +190,7 @@ public class GATKArgumentCollection { GATKArgumentCollection example = serializer.read(GATKArgumentCollection.class, file); return example; } catch (Exception e) { - throw new StingException("Failed to marshal the data to file " + file.toString(), e); + throw new StingException("Failed to marshal the data from file " + file.toString(), e); } } diff --git a/java/src/org/broadinstitute/sting/utils/GATKErrorReport.java b/java/src/org/broadinstitute/sting/utils/GATKErrorReport.java new file mode 100644 index 000000000..8720f23c0 --- /dev/null +++ b/java/src/org/broadinstitute/sting/utils/GATKErrorReport.java @@ -0,0 +1,77 @@ +package org.broadinstitute.sting.utils; + +import org.broadinstitute.sting.gatk.CommandLineGATK; +import org.broadinstitute.sting.gatk.GATKArgumentCollection; +import org.simpleframework.xml.Element; +import org.simpleframework.xml.ElementList; +import org.simpleframework.xml.Serializer; +import org.simpleframework.xml.core.Persister; +import org.simpleframework.xml.stream.Format; +import org.simpleframework.xml.stream.HyphenStyle; + +import java.io.PrintStream; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + + +/** + * @author aaron + *

+ * Class GATKErrorReport + *

+ * A basic description of what went wrong during a run of the GATK, + * what the parameters were, etc. + */ +public class GATKErrorReport { + // the listing of the fields is somewhat important; this is the order that the simple XML will output them + @ElementList(required = true, name = "gatk_header_Information") + private static List mGATKHeader; + + @Element(required = false, name = "exception") + private final ExceptionToXML mException; + + @Element(required = true, name = "date_time") + private final String mDateTime; + + @Element(required = true, name = "argument_collection") + private final GATKArgumentCollection mCollection; + + @Element(required = true, name = "working_directory") + private static String currentPath; + + private static final DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH.mm.ss"); + + static { + GATKErrorReport.mGATKHeader = CommandLineGATK.createApplicationHeader(); + currentPath = System.getProperty("user.dir"); + } + + public GATKErrorReport(Exception e, GATKArgumentCollection collection) { + this.mCollection = collection; + this.mException = new ExceptionToXML(e); + java.util.Date date = new java.util.Date(); + mDateTime = dateFormat.format(date); + } + + public void reportToStream(PrintStream stream) { + Serializer serializer = new Persister(new Format(new HyphenStyle())); + try { + serializer.write(this, stream); + } catch (Exception e) { + throw new StingException("Failed to marshal the data to the file " + stream, e); + } + } + + class ExceptionToXML { + @ElementList(required = false) + final List exceptionDetails = new ArrayList(); + + public ExceptionToXML(Exception e) { + for (StackTraceElement element : e.getStackTrace()) { + exceptionDetails.add(element.toString()); + } + } + } +} diff --git a/java/src/org/broadinstitute/sting/utils/cmdLine/CommandLineProgram.java b/java/src/org/broadinstitute/sting/utils/cmdLine/CommandLineProgram.java index ff7ccb6e2..128665202 100644 --- a/java/src/org/broadinstitute/sting/utils/cmdLine/CommandLineProgram.java +++ b/java/src/org/broadinstitute/sting/utils/cmdLine/CommandLineProgram.java @@ -2,13 +2,15 @@ package org.broadinstitute.sting.utils.cmdLine; import org.apache.log4j.*; +import java.io.File; import java.io.IOException; +import java.io.PrintStream; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; import java.util.Enumeration; -import java.util.Collections; -import java.util.Collection; /** * User: aaron @@ -29,76 +31,61 @@ import java.util.Collection; */ public abstract class CommandLineProgram { - /** - * The command-line program and the arguments it returned. - */ + /** The command-line program and the arguments it returned. */ private ParsingEngine parser = null; - /** - * our log, which we want to capture anything from org.broadinstitute.sting - */ + /** our log, which we want to capture anything from org.broadinstitute.sting */ private static Logger logger = Logger.getRootLogger(); - /** - * the default log level - */ - @Argument(fullName="logging_level", - shortName="l", - doc="Set the minimum level of logging, i.e. setting INFO get's you INFO up to FATAL, setting ERROR gets you ERROR and FATAL level logging. (DEBUG, INFO, WARN, ERROR, FATAL, OFF). ", - required=false) + /** the default log level */ + @Argument(fullName = "logging_level", + shortName = "l", + doc = "Set the minimum level of logging, i.e. setting INFO get's you INFO up to FATAL, setting ERROR gets you ERROR and FATAL level logging. (DEBUG, INFO, WARN, ERROR, FATAL, OFF). ", + required = false) protected String logging_level = "WARN"; - /** - * where to send the output of our logger - */ - @Argument(fullName="log_to_file", - shortName="log", - doc="Set the logging location", - required=false) + /** where to send the output of our logger */ + @Argument(fullName = "log_to_file", + shortName = "log", + doc = "Set the logging location", + required = false) protected String toFile = null; - /** - * do we want to silence the command line output - */ - @Argument(fullName="quiet_output_mode", - shortName="quiet", - doc="Set the logging to quiet mode, no output to stdout", - required=false) + /** do we want to silence the command line output */ + @Argument(fullName = "quiet_output_mode", + shortName = "quiet", + doc = "Set the logging to quiet mode, no output to stdout", + required = false) protected Boolean quietMode = false; - /** - * do we want to generate debugging information with the logs - */ - @Argument(fullName="debug_mode", - shortName="debug", - doc="Set the logging file string to include a lot of debugging information (SLOW!)", - required=false) + /** do we want to generate debugging information with the logs */ + @Argument(fullName = "debug_mode", + shortName = "debug", + doc = "Set the logging file string to include a lot of debugging information (SLOW!)", + required = false) protected Boolean debugMode = false; - /** - * this is used to indicate if they've asked for help - */ - @Argument(fullName="help",shortName="h",doc="Generate this help message",required=false) + /** this is used to indicate if they've asked for help */ + @Argument(fullName = "help", shortName = "h", doc = "Generate this help message", required = false) public Boolean help = false; - /** - * our logging output patterns - */ + /** our logging output patterns */ private static String patternString = "%-5p %d{HH:mm:ss,SSS} %C{1} - %m %n"; private static String debugPatternString = "%n[level] %p%n[date]\t\t %d{dd MMM yyyy HH:mm:ss,SSS} %n[class]\t\t %C %n[location]\t %l %n[line number]\t %L %n[message]\t %m %n"; /** * Allows a given application to return a brief description of itself. - * @return An ApplicationDetails object describing the current application. Should not be null. + * + * @return An ApplicationDetails object describing the current application. Should not be null. */ protected ApplicationDetails getApplicationDetails() { - return new ApplicationDetails( ApplicationDetails.createDefaultHeader(getClass()), - ApplicationDetails.createDefaultRunningInstructions(getClass()) ); + return new ApplicationDetails(ApplicationDetails.createDefaultHeader(getClass()), + ApplicationDetails.createDefaultRunningInstructions(getClass())); } /** - * Subclasses of CommandLinePrograms can provide their own types of command-line arguments. + * Subclasses of CommandLinePrograms can provide their own types of command-line arguments. * @return A collection of type descriptors generating implementation-dependent placeholders. */ protected Collection getArgumentTypeDescriptors() { @@ -109,19 +96,25 @@ public abstract class CommandLineProgram { * Will this application want to vary its argument list dynamically? * If so, parse the command-line options and then prompt the subclass to return * a list of argument providers. + * * @return Whether the application should vary command-line arguments dynamically. */ protected boolean canAddArgumentsDynamically() { return false; } /** * Provide a list of object to inspect, looking for additional command-line arguments. + * * @return A list of objects to inspect. */ - protected Class[] getArgumentSources() { return new Class[] {}; } + protected Class[] getArgumentSources() { + return new Class[]{}; + } /** * Name this argument source. Provides the (full) class name as a default. + * * @param source The argument source. + * * @return a name for the argument source. */ protected String getArgumentSourceName( Class source ) { return source.toString(); } @@ -129,7 +122,7 @@ public abstract class CommandLineProgram { /** * this is the function that the inheriting class can expect to have called * when all the argument processing is done - * + * * @return the return code to exit the program with */ protected abstract int execute(); @@ -145,7 +138,7 @@ public abstract class CommandLineProgram { * This function is called to start processing the command line, and kick * off the execute message of the program. * - * @param clp the command line program to execute + * @param clp the command line program to execute * @param args the command line arguments passed in */ public static void start(CommandLineProgram clp, String[] args) { @@ -156,10 +149,10 @@ public abstract class CommandLineProgram { // setup the parser ParsingEngine parser = clp.parser = new ParsingEngine(clp); - parser.addArgumentSource( clp.getClass() ); + parser.addArgumentSource(clp.getClass()); // process the args - if( clp.canAddArgumentsDynamically() ) { + if (clp.canAddArgumentsDynamically()) { // if the command-line program can toss in extra args, fetch them and reparse the arguments. parser.parse(args); @@ -167,28 +160,27 @@ public abstract class CommandLineProgram { // - InvalidArgument in case these arguments are specified by plugins. // - MissingRequiredArgument in case the user requested help. Handle that later, once we've // determined the full complement of arguments. - parser.validate( EnumSet.of(ParsingEngine.ValidationType.MissingRequiredArgument, - ParsingEngine.ValidationType.InvalidArgument) ); - parser.loadArgumentsIntoObject( clp ); + parser.validate(EnumSet.of(ParsingEngine.ValidationType.MissingRequiredArgument, + ParsingEngine.ValidationType.InvalidArgument)); + parser.loadArgumentsIntoObject(clp); Class[] argumentSources = clp.getArgumentSources(); - for( Class argumentSource: argumentSources ) - parser.addArgumentSource( clp.getArgumentSourceName(argumentSource), argumentSource ); + for (Class argumentSource : argumentSources) + parser.addArgumentSource(clp.getArgumentSourceName(argumentSource), argumentSource); parser.parse(args); - if( isHelpPresent( clp, parser ) ) - printHelpAndExit( clp, parser ); + if (isHelpPresent(clp, parser)) + printHelpAndExit(clp, parser); parser.validate(); - } - else { + } else { parser.parse(args); - if( isHelpPresent( clp, parser ) ) - printHelpAndExit( clp, parser ); + if (isHelpPresent(clp, parser)) + printHelpAndExit(clp, parser); parser.validate(); - parser.loadArgumentsIntoObject( clp ); + parser.loadArgumentsIntoObject(clp); } // if we're in debug mode, set the mode up @@ -208,7 +200,7 @@ public abstract class CommandLineProgram { // if they set the mode to quiet if (clp.quietMode) { - + // the only appender we should have is stdout, the following meathod is // deprecated, but the standard remove all appenders doesn't seem to work // TODO: find the right function @@ -241,61 +233,79 @@ public abstract class CommandLineProgram { //System.exit(result); // todo -- is this safe -- why exit here? I want to run the GATK like normal } catch (ArgumentException e) { - clp.parser.printHelp( clp.getApplicationDetails() ); + clp.parser.printHelp(clp.getApplicationDetails()); // Rethrow the exception to exit with an error. throw e; } catch (Exception e) { // we catch all exceptions here. if it makes it to this level, we're in trouble. Let's bail! // TODO: what if the logger is the exception? hmm... - logger.fatal("Exception caught by base Command Line Program, with message: " + e.getMessage()); - if ( e.getCause() != null ) logger.fatal("with cause: " + e.getCause()); - //e.printStackTrace(); + logger.fatal("Exception caught by base Command Line Program. Stack trace is as follows:"); + toErrorLog(clp, e); throw new RuntimeException(e); } } + /** + * generate an error log + * @param clp the command line program + * @param e the exception + */ + private static void toErrorLog(CommandLineProgram clp, Exception e) { + File logFile = new File("GATK_Error.log"); + PrintStream stream = null; + try { + stream = new PrintStream(logFile); + } catch (Exception e1) { // catch all the exceptions here, if we can't create the file, do the alternate path + if ( e.getCause() != null ) logger.fatal("with cause: " + e.getCause()); + throw new RuntimeException(e); + } + clp.generateErrorLog(stream, e); + } + /** * Find fields in the object obj that look like command-line arguments, and put command-line * arguments into them. + * * @param obj Object to inspect for command line arguments. */ - public void loadArgumentsIntoObject( Object obj ) { - parser.loadArgumentsIntoObject( obj ); + public void loadArgumentsIntoObject(Object obj) { + parser.loadArgumentsIntoObject(obj); } /** * a manual way to load argument providing objects into the program + * * @param clp the command line program * @param cls the class to load the arguments off of */ - public void loadAdditionalSource(CommandLineProgram clp, Class cls ) { - parser.addArgumentSource( clp.getArgumentSourceName(cls), cls ); + public void loadAdditionalSource(CommandLineProgram clp, Class cls) { + parser.addArgumentSource(clp.getArgumentSourceName(cls), cls); } /** * generateHeaderInformation *

- * + *

* Generate a standard header for the logger - * @param clp the command line program to execute - * @param args the command line arguments passed in * - **/ + * @param clp the command line program to execute + * @param args the command line arguments passed in + */ protected static void generateHeaderInformation(CommandLineProgram clp, String[] args) { DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); java.util.Date date = new java.util.Date(); logger.info("-------------------------------------------------------"); - for( String headerLine: clp.getApplicationDetails().applicationHeader ) + for (String headerLine : clp.getApplicationDetails().applicationHeader) logger.info(headerLine); String output = ""; for (String str : args) { output = output + str + " "; } logger.info("Program Args: " + output); - logger.info("Time/Date: " + dateFormat.format(date)); + logger.info("Date/Time: " + dateFormat.format(date)); logger.info("-------------------------------------------------------"); } @@ -303,27 +313,21 @@ public abstract class CommandLineProgram { * this function checks the logger level passed in on the command line, taking the lowest * level that was provided. */ - private void setupLoggerLevel() { + private void setupLoggerLevel() { Level par = Level.WARN; if (logging_level.toUpperCase().equals("DEBUG")) { par = Level.DEBUG; - } - else if (logging_level.toUpperCase().equals("ERROR")) { + } else if (logging_level.toUpperCase().equals("ERROR")) { par = Level.ERROR; - } - else if (logging_level.toUpperCase().equals("FATAL")) { + } else if (logging_level.toUpperCase().equals("FATAL")) { par = Level.FATAL; - } - else if (logging_level.toUpperCase().equals("INFO")) { + } else if (logging_level.toUpperCase().equals("INFO")) { par = Level.INFO; - } - else if (logging_level.toUpperCase().equals("WARN")) { + } else if (logging_level.toUpperCase().equals("WARN")) { par = Level.WARN; - } - else if (logging_level.toUpperCase().equals("OFF")) { + } else if (logging_level.toUpperCase().equals("OFF")) { par = Level.OFF; - } - else { + } else { // we don't understand the logging level, let's get out of here throw new ArgumentException("Unable to match: " + logging_level + " to a logging level, make sure it's a valid level (INFO, DEBUG, ERROR, FATAL, OFF)"); } @@ -348,26 +352,30 @@ public abstract class CommandLineProgram { /** * Do a cursory search for the given argument. - * @param clp Instance of the command-line program. + * + * @param clp Instance of the command-line program. * @param parser Parser + * * @return True if help is present; false otherwise. */ - private static boolean isHelpPresent( CommandLineProgram clp, ParsingEngine parser ) { + private static boolean isHelpPresent(CommandLineProgram clp, ParsingEngine parser) { return parser.isArgumentPresent("help"); } /** * Print help and exit. - * @param clp Instance of the command-line program. + * + * @param clp Instance of the command-line program. * @param parser True if help is present; false otherwise. */ - private static void printHelpAndExit( CommandLineProgram clp, ParsingEngine parser ) { - parser.printHelp( clp.getApplicationDetails() ); + private static void printHelpAndExit(CommandLineProgram clp, ParsingEngine parser) { + parser.printHelp(clp.getApplicationDetails()); System.exit(0); } /** * used to indicate an error occured + * * @param msg the message to display */ public static void exitSystemWithError(final String msg) { @@ -377,8 +385,9 @@ public abstract class CommandLineProgram { /** * used to indicate an error occured + * * @param msg the message - * @param e the error + * @param e the error */ public static void exitSystemWithError(final String msg, Exception e) { e.printStackTrace(); @@ -388,9 +397,20 @@ public abstract class CommandLineProgram { /** * used to indicate an error occured + * * @param e the exception occured */ public static void exitSystemWithError(Exception e) { exitSystemWithError(e.getMessage(), e); } + + /** + * generate an error log, given the stream to write to and the execption that was generated + * + * @param stream the output stream + * @param e the exception + */ + public void generateErrorLog(PrintStream stream, Exception e) { + stream.println(e.getStackTrace().toString()); + } }