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
102 StartUpOptions options = new StartUpOptions();
103 try {
104 options.setArgs(args);
105 } catch (IllegalArgumentException iae) {
106 showUsage();
107 }
108
109 LogAnalyzerBatch batch = new LogAnalyzerBatch();
110 batch.setOptions(options);
111
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
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
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
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
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
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
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
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
311 logger.debug("...KO : ", ie);
312 System.err.println("Can't parse and analysis : " + ie.getMessage());
313 System.exit(1);
314 }
315
316 handler.removeLogHandlerListener(this);
317 if (handler.getLastException() == null) {
318
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
352 if (getOptions().isOptionShowGUI() && handler != null
353 && handler.getSize() > 0) {
354
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]<<i>;report name</i>>.<<i>report file extension</i>>
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 }