View Javadoc
1   package net.logAnalyzer;
2   
3   import java.io.File;
4   import java.io.FileNotFoundException;
5   import java.io.IOException;
6   import java.util.Vector;
7   
8   import net.logAnalyzer.analysis.LAAnalyzer;
9   import net.logAnalyzer.config.CommandsManager;
10  import net.logAnalyzer.config.StartUpOptions;
11  import net.logAnalyzer.gui.splash.SplashWindow;
12  import net.logAnalyzer.handlers.ForwardOnlyLogHandler;
13  import net.logAnalyzer.handlers.LALogHandler;
14  import net.logAnalyzer.handlers.LALogHandlerListener;
15  import net.logAnalyzer.handlers.MemoryLogHandler;
16  import net.logAnalyzer.handlers.NoConverterException;
17  import net.logAnalyzer.handlers.OnDemandLogHandler;
18  import net.logAnalyzer.reports.LAReport;
19  import net.logAnalyzer.reports.LAReportRenderer;
20  import net.logAnalyzer.reports.ReportException;
21  
22  import org.apache.log4j.Logger;
23  
24  /***
25   * This class is the command line executable for Log LAAnalyzer. It is used to
26   * analyze the specified log file and to launch the GUI according to command
27   * lines options.
28   * <p>
29   * <b>Threaded log file analysis</b>
30   * </p>
31   * <p>
32   * The log file analysis is launch in a new {@link java.lang.Thread} to allow
33   * progress logging using the following
34   * {@link net.logAnalyzer.handlers.LALogHandlerListener} events :
35   * </p>
36   * <ul>
37   * <li>{@link net.logAnalyzer.handlers.LALogHandlerListener#startParsing(LALogHandler)}</li>
38   * <li>{@link net.logAnalyzer.handlers.LALogHandlerListener#stepParsing(LALogHandler)}</li>
39   * <li>{@link net.logAnalyzer.handlers.LALogHandlerListener#endParsing(LALogHandler)}</li>
40   * </ul>
41   * <p>
42   * <b>Command line options </b>
43   * </p>
44   * <p>
45   * <code>java -jar logAnalyzer.jar LogAnalyzerBatch [-gui] [-reportsprefix prefix] <i>commandsfile.xml</i> <i>logfile+</i> </code>
46   * </p>
47   * <table>
48   * <tr>
49   * <th>Argument</th>
50   * <th>Description</th>
51   * </tr>
52   * <tr>
53   * <td><i>-gui </i></td>
54   * <td>Starts the GUI after analyzing and reports rendering processes.</td>
55   * </tr>
56   * <tr>
57   * <td><i>-reportsprefix prefix </i></td>
58   * <td>Adds this prefix to each generated report file.</td>
59   * </tr>
60   * <tr>
61   * <td><i>commandsfile.xml </i></td>
62   * <td>XML file containing options and commands to execute during parsing and
63   * analysis of the log file (see documentation for details). </td>
64   * </tr>
65   * <tr>
66   * <td><i>logfile(s) </i></td>
67   * <td>One or more log files to parse and analysis. Each line of the file will
68   * be parsed using the <i>pattern </i> (specified in the XML commands file).
69   * <br>
70   * If more than one log file is specified, they are parsed as one big file.</td>
71   * </tr>
72   * </table>
73   * <p>
74   * <b>Content of the XML commands file </b>
75   * </p>
76   * <p>
77   * This file contains options and commands according to the DTD
78   * <i>LogAnalyzeCommands.dtd </i> (see this DTD for details).
79   * </p>
80   */
81  public final class LogAnalyzerBatch implements LALogHandlerListener {
82      /***
83       * Sleep time used to wait during parsing.
84       */
85      private static final int SLEEPTIME = 1000;
86  
87      /***
88       * Class logger.
89       */
90      private static Logger logger = Logger.getLogger(LogAnalyzerBatch.class
91              .getName());
92  
93      /***
94       * Entry point. Gets the command line arguments and runs a new
95       * {@link LogAnalyzerBatch} instance.
96       * 
97       * @param args
98       *            Command line arguments.
99       */
100     public static void main(final String[] args) {
101         // Gets the command line arguments
102         StartUpOptions options = new StartUpOptions();
103         try {
104             options.setArgs(args);
105         } catch (IllegalArgumentException iae) {
106             showUsage();
107         }
108         // Creates a new batch instance
109         LogAnalyzerBatch batch = new LogAnalyzerBatch();
110         batch.setOptions(options);
111         // Runs the new batch
112         batch.run();
113     }
114 
115     /***
116      * Prints the LogAnalyzerBatch usage rules and terminates the running JVM.
117      */
118     private static void showUsage() {
119         System.err
120                 .println("Usage: java logAnalyzer.jar LogAnalyzerBatch [-gui]  [-reportsfile prefix]  commandsfile.xml logfile [logfile2 [logfile3...]]");
121         System.err.println();
122         System.err.println("\t -gui \t Opens the gui at end of analyze.");
123         System.err
124                 .println("\t -reportsprefix prefix \t Prefix used for reports files saved.");
125         System.err
126                 .println("\t commandsfile.xml \t XML file containing options and commands to execute during parsing and analysis of the log file (see documentation for details).");
127         System.err.println("\t logfile \t\t Log file to parse and analysis.");
128         System.err
129                 .println("\t logfileN \t\t If more than one log file, log files are read as a single large log file.");
130         System.err
131                 .println(" It is better but not required than log files are ordered from oldest (the first in the command line arguments order) to newest (the last argument).");
132         System.exit(-1);
133     }
134 
135     /***
136      * Start up options.
137      */
138     private static StartUpOptions options = null;
139 
140     /***
141      * Is <code>true</code> if the parsing is done.
142      */
143     private boolean parsingDone = false;
144 
145     /***
146      * Default constructor.
147      */
148     public LogAnalyzerBatch() {
149         // NOP
150     }
151 
152     /***
153      * Start up options.
154      * 
155      * @return Returns the options.
156      */
157     public static StartUpOptions getOptions() {
158         return options;
159     }
160 
161     /***
162      * Sets the start up options.
163      * 
164      * @param options
165      *            The options to set.
166      */
167     public void setOptions(StartUpOptions options) {
168         this.options = options;
169     }
170 
171     /***
172      * Called by a {@link LALogHandler} when the parsing process starts. Sets
173      * internal properties.
174      * 
175      * @param logHandler
176      *            Log handler currently prased.
177      * @see LALogHandlerListener#startParsing(LALogHandler)
178      */
179     public void startParsing(LALogHandler logHandler) {
180         parsingDone = false;
181         logger.debug("Starts parsing");
182         fireMessage("Starts parsing");
183     }
184 
185     /***
186      * Called by a {@link LALogHandler} when the parsing process reaches a step
187      * beyond. Sets internal properties and show parsing progression in debug
188      * mode.
189      * 
190      * @param logHandler
191      *            Log handler currently parsed.
192      * @see LALogHandlerListener#stepParsing(LALogHandler)
193      */
194     public void stepParsing(LALogHandler logHandler) {
195         if (logger.isDebugEnabled()) {
196             int newPercents = logHandler.getPercentDone();
197             if (newPercents != 0) {
198                 logger.debug(String.valueOf(newPercents) + "% done");
199                 fireMessage("Parsing in progress");
200                 firePercent(newPercents);
201             }
202         }
203     }
204 
205     /***
206      * Called by a {@link LALogHandler} when the parsing is done. Sets internal
207      * properties.
208      * 
209      * @param logHandler
210      *            Log handler currently parsed.
211      * @see net.logAnalyzer.handlers.LALogHandlerListener#endParsing(net.logAnalyzer.handlers.LALogHandler)
212      */
213     public void endParsing(LALogHandler logHandler) {
214         parsingDone = true;
215         logger.debug("100% done");
216         fireMessage("Parsing 100% done");
217     }
218 
219     /***
220      * Runs the analysis. Creates a {@link LALogHandler}, and start it in a new
221      * {@link Thread}.
222      * <p>
223      * The {@link LALogHandler} implementation used is depending on several
224      * criteria :
225      * <ul>
226      * <li>If the {@link StartUpOptions#isOptionShowGUI()} is not set, uses the
227      * {@link ForwardOnlyLogHandler} ; </li>
228      * <li>If the {@link StartUpOptions#isOptionShowGUI()} is set and the log
229      * file(s) size is less than 10 Mb, uses the
230      * {@link net.logAnalyzer.handlers.MemoryLogHandler} for best response times</li>
231      * <li>In others cases, uses the {@link OnDemandLogHandler}</li>
232      * </ul>
233      * </p>
234      * 
235      * @see LALogHandler
236      */
237     private synchronized void run() {
238         if (getOptions().isOptionShowGUI()) {
239             SplashWindow.createAndShowGUI(this);
240         }
241         // Parse and analyze the pattern
242         LAAnalyzer[] analyzers = null;
243         LAReportRenderer[] renderers = null;
244         fireMessage("Gets the pattern and commands from commands file ["
245                 + getOptions().getCommandsFilename() + "]...");
246         logger.debug("Gets the pattern and commands from commands file ["
247                 + getOptions().getCommandsFilename() + "]...");
248         CommandsManager commandsMng = CommandsManager
249                 .createInstance(getOptions().getCommandsFilename());
250         String pattern = commandsMng.getPattern();
251         analyzers = commandsMng.getAnalyzers();
252         renderers = commandsMng.getRenderers();
253         fireMessage(analyzers.length + " analyzer(s) " + renderers.length
254                 + " renderer(s)");
255         logger.debug("...OK : " + analyzers.length + " analyzer(s) "
256                 + renderers.length + " renderer(s)");
257         // Creates the log handler
258         LALogHandler handler = null;
259         fireMessage("Creates the handler and set analyzers...");
260         logger.debug("Creates the handler and set analyzers...");
261         try {
262             if (!getOptions().isOptionShowGUI()) {
263                 handler = new ForwardOnlyLogHandler(getOptions().getLogFiles(),
264                         pattern);
265             } else if (getOptions().getLogFiles().length() < MemoryLogHandler.MAXSIZE_RECOMMENDED) {
266                 handler = new MemoryLogHandler(getOptions().getLogFiles(),
267                         pattern);
268             } else {
269                 handler = new OnDemandLogHandler(getOptions().getLogFiles(),
270                         pattern);
271             }
272         } catch (FileNotFoundException fnfe) {
273             // NOP
274             logger.debug("...KO : ", fnfe);
275             System.err.println("Can't parse and analysis : "
276                     + fnfe.getMessage());
277             System.exit(1);
278         } catch (NoConverterException nce) {
279             // NOP
280             logger.debug("...KO : ", nce);
281             System.err
282                     .println("Can't parse and analysis : " + nce.getMessage());
283             System.exit(1);
284         }
285         handler.setAnalyzers(analyzers);
286         logger.debug("...OK");
287         // Creates and launches the analyzing thread
288         fireMessage("Creates and launches the analyzing thread...");
289         logger.debug("Creates and launches the analyzing thread...");
290         handler.addLogHandlerListener(this);
291         Thread handlerThread = new Thread(handler);
292         handlerThread.start();
293         parsingDone = false;
294         try {
295             // Parsing loop
296             while (!parsingDone) {
297                 wait(SLEEPTIME);
298             }
299             if (handler != null && handler.getSize() > 0) {
300                 logger.debug("...OK : " + handler.getSize()
301                         + " messages parsed and analyzed");
302             } else if (handler != null && handler.getSize() == 0) {
303                 logger.debug("...KO : no message could be analyzed");
304                 System.err
305                         .println("...KO : no message could be analyzed, please check your XML command file, the pattern used to parse the file is perhaps not the right one.");
306                 System.exit(1);
307             }
308 
309         } catch (InterruptedException ie) {
310             // NOP
311             logger.debug("...KO : ", ie);
312             System.err.println("Can't parse and analysis : " + ie.getMessage());
313             System.exit(1);
314         }
315         // Stops listening
316         handler.removeLogHandlerListener(this);
317         if (handler.getLastException() == null) {
318             // Renders analyzers results
319             Vector reports = new Vector();
320             if (analyzers != null && renderers != null) {
321                 LAReport[] rendererReports = null;
322                 try {
323                     for (int i = 0; i < renderers.length; i++) {
324                         LAReportRenderer renderer = renderers[i];
325                         fireMessage("Rendering report [" + renderer.getName()
326                                 + "]...");
327                         logger.debug("Rendering report [" + renderer.getName()
328                                 + "]...");
329                         rendererReports = renderer.render();
330                         saveReports(getOptions().getReportsDirectory(),
331                                 rendererReports, getOptions()
332                                         .getReportsPrefix());
333                         for (int j = 0; j < rendererReports.length; j++) {
334                             reports.add(rendererReports[j]);
335                         }
336                         logger.debug("...OK");
337                     }
338                 } catch (IOException ioe) {
339                     System.err.println("Can't save reports : "
340                             + ioe.getMessage());
341                     logger.debug("...KO : ", ioe);
342                     System.exit(1);
343                 } catch (ReportException re) {
344                     System.err.println("Can't generate reports : "
345                             + re.getMessage());
346                     re.printStackTrace(System.err);
347                     logger.debug("...KO : ", re);
348                     System.exit(1);
349                 }
350             }
351             // Launches GUI if needed
352             if (getOptions().isOptionShowGUI() && handler != null
353                     && handler.getSize() > 0) {
354                 // Creates the frame
355                 LAReport[] renderersReports = new LAReport[reports.size()];
356                 renderersReports = (LAReport[]) reports
357                         .toArray(renderersReports);
358                 SplashWindow.closeGUI(this);
359                 LogAnalyzerGUI.launchGUI(handler, renderersReports);
360             }
361         }
362     }
363 
364     /***
365      * Save the specified report in the specified directory. Each report file
366      * name is defined as : <code>
367      *     [prefix]&lt;<i>;report name</i>&gt;.&lt;<i>report file extension</i>&gt;
368      * </code>
369      * 
370      * @param reportDir
371      *            Directory where to save reports files.
372      * @param reports
373      *            Reports to save.
374      * @param prefix
375      *            Prefix used in files names.
376      * @throws IOException
377      *             If an I/O error occurs.
378      */
379     private void saveReports(File reportDir, LAReport[] reports, String prefix)
380             throws IOException {
381         if (reports != null) {
382             for (int i = 0; i < reports.length; i++) {
383                 String reportName = reports[i].getName();
384                 fireMessage("Saving report [" + reportName + "] ...");
385                 if (prefix.length() > 0) {
386                     reportName = prefix + reportName;
387                 }
388                 String extension = "." + reports[i].getFileExtension();
389                 String directory = reportDir.getAbsolutePath()
390                         + System.getProperty("file.separator");
391                 String reportFile = directory + reportName + extension;
392                 logger.debug("   -> saves [" + reportFile + "]");
393                 try {
394                     reports[i].saveToFile(reportFile);
395                 } catch (IOException ioe) {
396                     reportFile = File.createTempFile(reportName + "_",
397                             extension, reportDir).getAbsolutePath();
398                     logger.error("   -> saves [" + reportFile + "]");
399                     reports[i].saveToFile(reportFile);
400                 }
401             }
402         }
403     }
404 
405     /***
406      * Listeners for logAnalyzer events.
407      * 
408      * @see LALogHandlerListener
409      */
410     private Vector logAnalyzerListeners = new Vector();
411 
412     /***
413      * Adds a listener.
414      * 
415      * @param listener
416      *            Lister to add.
417      */
418     public void addLogAnalyzerListener(LogAnalyzerBatchListener listener) {
419         if (!logAnalyzerListeners.contains(listener)) {
420             logAnalyzerListeners.add(listener);
421         }
422     }
423 
424     /***
425      * Removes a listener.
426      * 
427      * @param listener
428      *            Lister to remove.
429      */
430     public void removeLogAnalyzerListener(LogAnalyzerBatchListener listener) {
431         logAnalyzerListeners.remove(listener);
432     }
433 
434     /***
435      * Fires a message event.
436      * 
437      * @param message
438      *            Message for which to fire an event.
439      */
440     public void fireMessage(String message) {
441         for (int i = 0; i < logAnalyzerListeners.size(); i++) {
442             ((LogAnalyzerBatchListener) logAnalyzerListeners.get(i))
443                     .reportCurrentTask(message);
444         }
445     }
446 
447     /***
448      * Fires a percent event.
449      * 
450      * @param percent
451      *            Percentages.
452      */
453     public void firePercent(int percent) {
454         for (int i = 0; i < logAnalyzerListeners.size(); i++) {
455             ((LogAnalyzerBatchListener) logAnalyzerListeners.get(i))
456                     .reportPercent(percent);
457         }
458     }
459 }