001 package com.softnetConsult.utils.log;
002
003 import java.io.File;
004 import java.io.FileNotFoundException;
005 import java.io.FileOutputStream;
006 import java.io.UnsupportedEncodingException;
007
008 import com.softnetConsult.utils.exceptions.ThrowableTools;
009 import com.softnetConsult.utils.streams.MultiPrintStream;
010
011
012 /**
013 * This class provides a convenient way to direct text output to the screen (std-out)
014 * and to a file (file stream) at the same time, in addition it provides an easy
015 * way to make sure that all output is indented according to a specifiable level;
016 * this makes this class ideal for simple logging and tracking tasks.
017 *
018 * <p style="font-size:smaller;">This product includes software developed by the
019 * <strong>SoftNet-Consult Java Utility Library</strong> project and its contributors.<br />
020 * (<a href="http://java-tools.sourceforge.net" target="_blank">http://java-tools.sourceforge.net</a>)<br />
021 * Copyright (c) 2007-2008 SoftNet-Consult.<br />
022 * Copyright (c) 2007-2008 G. Paperin.<br />
023 * All rights reserved.
024 * </p>
025 * <p style="font-size:smaller;">File: ScreenFileLogger.java<br />
026 * Library API version: {@value com.softnetConsult.utils.APIProperties#apiVersion}<br />
027 * Java compliance version: {@value com.softnetConsult.utils.APIProperties#javaComplianceVersion}
028 * </p>
029 * <p style="font-size:smaller;">Redistribution and use in source and binary forms, with or
030 * without modification, are permitted provided that the following terms and conditions are met:
031 * </p>
032 * <p style="font-size:smaller;">1. Redistributions of source code must retain the above
033 * acknowledgement of the SoftNet-Consult Java Utility Library project, the above copyright
034 * notice, this list of conditions and the following disclaimer.<br />
035 * 2. Redistributions in binary form must reproduce the above acknowledgement of the
036 * SoftNet-Consult Java Utility Library project, the above copyright notice, this list of
037 * conditions and the following disclaimer in the documentation and/or other materials
038 * provided with the distribution.<br />
039 * 3. All advertising materials mentioning features or use of this software or any derived
040 * software must display the following acknowledgement:<br />
041 * <em>This product includes software developed by the SoftNet-Consult Java Utility Library
042 * project and its contributors.</em>
043 * </p>
044 * <p style="font-size:smaller;">THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY
045 * OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
046 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
047 * THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
048 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
049 * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
050 * </p>
051 * @author Greg Paperin (<a href="http://www.paperin.org" target="_blank">http://www.paperin.org</a>)
052 * @version {@value com.softnetConsult.utils.APIProperties#apiVersion}
053 */
054 public class ScreenFileLogger {
055
056 /**
057 * Maximum indentation level permitted.
058 */
059 public static final int MAX_INDENT_LEVEL = 250;
060
061 /**
062 * Indentation string (one copy per level).
063 */
064 private String indentation;
065
066 /**
067 * Whether to indent next output.
068 */
069 private boolean indentNext;
070
071 /**
072 * Current indentation level.
073 */
074 private int indentLevel;
075
076
077 /**
078 * Output file.
079 */
080 private File logFile;
081
082 /**
083 * Output file stream.
084 */
085 private FileOutputStream fileStream;
086
087 /**
088 * Whether to append or overwrite when output file is (re)opened.
089 */
090 private boolean fileAppend;
091
092
093 /**
094 * Aggregating output stream.
095 */
096 private MultiPrintStream outs;
097
098
099 /**
100 * Creates a logger that can write all logged output to screen (or whatever is the
101 * current std-out) and/or to a file.
102 *
103 * @param logToScreen Whether to start logging to std-out (can be changed later).
104 * @param logToFile Whether to start logging to a file (can be changed later if file is specified).
105 * @param logFileName File name to write to.
106 * @param append Append or overwrite the file.
107 * @param indentation Indentation string (one copy per indentation level).
108 * @throws FileNotFoundException If there is a problem when opening the specified file for writing.
109 */
110 public ScreenFileLogger(boolean logToScreen,
111 boolean logToFile, String logFileName, boolean append,
112 String indentation)
113 throws FileNotFoundException {
114 this( logToScreen, logToFile,
115 (null == logFileName ? (File) null : new File(logFileName)),
116 append, indentation);
117 }
118
119 /**
120 * Creates a logger that can write all logged output to screen (or whatever is the
121 * current std-out) and/or to a file.
122 *
123 * @param logToScreen Whether to start logging to std-out (can be changed later).
124 * @param logToFile Whether to start logging to a file (can be changed later if file is specified).
125 * @param logFile File to write to.
126 * @param append Append or overwrite the file.
127 * @param indentation Indentation string (one copy per indentation level).
128 * @throws FileNotFoundException If there is a problem when opening the specified file for writing.
129 */
130 public ScreenFileLogger(boolean logToScreen,
131 boolean logToFile, File logFile, boolean append,
132 String indentation)
133 throws FileNotFoundException {
134
135 if (null == logFile && logToFile)
136 throw new IllegalArgumentException("logFile cannot be null if logging to file is enabled");
137
138 this.indentation = (null == indentation ? "" : indentation);
139 this.indentNext = true;
140 this.indentLevel = 0;
141
142 this.logFile = logFile;
143 this.fileStream = null;
144 this.fileAppend = append;
145
146 this.outs = new MultiPrintStream();
147
148 setLogToScreen(logToScreen);
149 setLogToFile(logToFile);
150 }
151
152 /**
153 * Gets the file associated with this logger (regardless of whether this logger currently
154 * logs to a file).
155 *
156 * @return File associated with this logger.
157 */
158 public File getFile() {
159 return logFile;
160 }
161
162 /**
163 * Checks whether this logger currently writes to the screen (std-out).
164 *
165 * @return Whether this logger currently writes to the screen (std-out).
166 */
167 public boolean getLogToScreen() {
168 return outs.hasOutput(System.out);
169 }
170
171 /**
172 * Sets whether this logger will write to the screen (std-out) for the subsequent
173 * print-actions.
174 *
175 * @param logToScreen Whther to write to {@code System.out} or not.
176 */
177 public void setLogToScreen(boolean logToScreen) {
178 if (logToScreen)
179 outs.addOutputs(System.out);
180 else
181 outs.removeOutput(System.out, false);
182 }
183
184 /**
185 * Checks whether this logger currently writes to a file.
186 *
187 * @return Whether this logger currently writes to a file.
188 */
189 public boolean getLogToFile() {
190 return outs.hasOutput(fileStream);
191 }
192
193 /**
194 * Sets whether this logger will write to a for the subsequent print-actions.
195 * The file must be specified in the constructor, if it was set to {@code null},
196 * an attempt to turn file-logging on will raise an {@code IllegalArgumentException}.<br />
197 * If file-writing is being turned on and this logger has not previously written to
198 * a file, the data will be overwritten or appended according to the {@code append}
199 * parameter in the constructor.<br />
200 * If the file-writing was on and is then turned off, the file is closed.<br />
201 * If the file-writing is subsequently turned on again, the file is re-opened
202 * and data is appended at the end.
203 *
204 * @param logToFile Whether to log to a file or not.
205 * @throws FileNotFoundException If there is a problem when opening the file.
206 */
207 public void setLogToFile(boolean logToFile) throws FileNotFoundException {
208
209 if (outs.hasOutput(fileStream) == logToFile)
210 return;
211
212 if (logToFile) {
213
214 if (null == logFile)
215 throw new IllegalArgumentException("Cannot log to file because logFile is null");
216
217 fileStream = new FileOutputStream(logFile, fileAppend);
218 try { outs.addOutputs(true, "UTF-8", fileStream); }
219 catch (UnsupportedEncodingException e) {
220 throw new RuntimeException("Weird, the encoding \"UTF-8\" is not available", e);
221 }
222 fileAppend = true;
223 } else {
224 outs.removeOutput(fileStream, true);
225 fileStream = null;
226 }
227 }
228
229 /**
230 * Closes this logger and a possibly open file stream.
231 */
232 public void close() {
233 if (null != outs) {
234 outs.flush();
235 outs.closeExcept(System.out);
236 outs = null;
237 }
238 }
239
240 /**
241 * Ensures that all associated streams are closed when this logger is finalised.
242 */
243 @Override
244 protected void finalize() throws Throwable {
245 this.close();
246 super.finalize();
247 }
248
249 /**
250 * Sets the indentation level to the specicied value.
251 * Each line following a new-line will begin with {@code indentLevel} copies of
252 * the {@code indentation}-string.
253 *
254 * @param indentLevel The new indentation level.
255 */
256 public void setIndentLevel(int indentLevel) {
257 if (0 > indentLevel)
258 throw new IllegalArgumentException("0 > indentLevel is not allowed");
259 if (MAX_INDENT_LEVEL < indentLevel)
260 throw new IllegalArgumentException("MAX_INDENT_LEVEL < indentLevel is not allowed (MAX_INDENT_LEVEL="
261 + MAX_INDENT_LEVEL + ")");
262
263 this.indentLevel = indentLevel;
264 }
265
266 /**
267 * Gets the current indentation level.
268 *
269 * @return The current indentation level.
270 */
271 public int getIndentLevel() {
272 return this.indentLevel;
273 }
274
275 /**
276 * Increments the current indentation level.
277 * Each line following a new-line will begin with {@code indentLevel} copies of
278 * the {@code indentation}-string.
279 */
280 public void incIndentLevel() {
281 if (MAX_INDENT_LEVEL == indentLevel)
282 throw new IllegalStateException("May not increase indentLevel when it is MAX_INDENT_LEVEL"
283 + " (MAX_INDENT_LEVEL=" + MAX_INDENT_LEVEL + ")");
284 this.indentLevel++;
285 }
286
287 /**
288 * Decrements the current indentation level.
289 * Each line following a new-line will begin with {@code indentLevel} copies of
290 * the {@code indentation}-string.
291 */
292 public void decIndentLevel() {
293 if (0 == indentLevel)
294 throw new IllegalStateException("May not decrease indentLevel when it is zero");
295 this.indentLevel--;
296 }
297
298 /**
299 * Used internally to generate a correct indentation string.
300 */
301 private void doIndentation() {
302 if (!indentNext)
303 return;
304 StringBuffer s = new StringBuffer();
305 for (int i = 0; i < indentLevel; i++)
306 s.append(indentation);
307 indentNext = false;
308 print(s.toString());
309 }
310
311 /**
312 * Logs a formated string to std-out and/or file following the format specification
313 * defined for {@link java.lang.String#format(String, Object[])}.
314 *
315 * @param format The format string.
316 * @param args The value arguments.
317 */
318 public void printf(String format, Object ... args) {
319 doIndentation();
320 outs.printf(format, args);
321 }
322
323 /**
324 * Prints an object to std-out and/or file.
325 *
326 * @param o Object to print.
327 */
328 public void print(Object o) {
329 doIndentation();
330 outs.print(o);
331 }
332
333 /**
334 * Prints extended information about a {@code Thowable}
335 * (including stack trace) to std-out and/or file.
336 *
337 * @param t Object to print.
338 */
339 public void print(Throwable t) {
340 String s = ThrowableTools.stackTraceToString(t);
341 doIndentation();
342 outs.print(s);
343 }
344
345 /**
346 * Prints a string to std-out and/or file.
347 *
348 * @param s String to print.
349 */
350 public void print(String s) {
351 doIndentation();
352 outs.print(s);
353 }
354
355 /**
356 * Prints a string to std-out and/or file.
357 *
358 * @param s String to print.
359 */
360 public void print(char[] s) {
361 doIndentation();
362 outs.print(s);
363 }
364
365 /**
366 * Prints the specified value to std-out and/or file.
367 *
368 * @param val The value to print.
369 */
370 public void print(boolean val) {
371 doIndentation();
372 outs.print(val);
373 }
374
375 /**
376 * Prints the specified value to std-out and/or file.
377 *
378 * @param val The value to print.
379 */
380 public void print(char val) {
381 doIndentation();
382 outs.print(val);
383 }
384
385 /**
386 * Prints the specified value to std-out and/or file.
387 *
388 * @param val The value to print.
389 */
390 public void print(int val) {
391 doIndentation();
392 outs.print(val);
393 }
394
395 /**
396 * Prints the specified value to std-out and/or file.
397 *
398 * @param val The value to print.
399 */
400 public void print(long val) {
401 doIndentation();
402 outs.print(val);
403 }
404
405 /**
406 * Prints the specified value to std-out and/or file.
407 *
408 * @param val The value to print.
409 */
410 public void print(float val) {
411 doIndentation();
412 outs.print(val);
413 }
414
415 /**
416 * Prints the specified value to std-out and/or file.
417 *
418 * @param val The value to print.
419 */
420 public void print(double val) {
421 doIndentation();
422 outs.print(val);
423 }
424
425 /**
426 * Initiates a new line by emitting a new-line character to std-out and/or file.
427 * Next output will be indented accoring to the current indentation level.
428 */
429 public void println() {
430 doIndentation();
431 outs.println();
432 indentNext = true;
433 }
434
435 /**
436 * Prints an object followed by a new line to std-out and/or file.
437 * Next output will be indented accoring to the current indentation level.
438 *
439 * @param o Object to print.
440 */
441 public void println(Object o) {
442 doIndentation();
443 outs.println(o);
444 indentNext = true;
445 }
446
447 /**
448 * Prints extended information about a {@code Thowable} (including stack
449 * trace) followed by a new line to std-out and/or file.
450 * Next output will be indented accoring to the current indentation level.
451 *
452 * @param t Object to print.
453 */
454 public void println(Throwable t) {
455 String s = ThrowableTools.stackTraceToString(t);
456 doIndentation();
457 outs.println(s);
458 indentNext = true;
459 }
460
461 /**
462 * Prints a string followed by a new line to std-out and/or file.
463 * Next output will be indented accoring to the current indentation level.
464 *
465 * @param s Object to print.
466 */
467 public void println(String s) {
468 doIndentation();
469 outs.println(s);
470 indentNext = true;
471 }
472
473 /**
474 * Prints a string followed by a new line to std-out and/or file.
475 * Next output will be indented accoring to the current indentation level.
476 *
477 * @param s Object to print.
478 */
479 public void println(char[] s) {
480 doIndentation();
481 outs.println(s);
482 indentNext = true;
483 }
484
485 /**
486 * Prints the specified value followed by a new line to std-out and/or file.
487 * Next output will be indented accoring to the current indentation level.
488 *
489 * @param val Value to print.
490 */
491 public void println(boolean val) {
492 doIndentation();
493 outs.println(val);
494 indentNext = true;
495 }
496
497 /**
498 * Prints the specified value followed by a new line to std-out and/or file.
499 * Next output will be indented accoring to the current indentation level.
500 *
501 * @param val Value to print.
502 */
503 public void println(char val) {
504 doIndentation();
505 outs.println(val);
506 indentNext = true;
507 }
508
509 /**
510 * Prints the specified value followed by a new line to std-out and/or file.
511 * Next output will be indented accoring to the current indentation level.
512 *
513 * @param val Value to print.
514 */
515 public void println(int val) {
516 doIndentation();
517 outs.println(val);
518 indentNext = true;
519 }
520
521 /**
522 * Prints the specified value followed by a new line to std-out and/or file.
523 * Next output will be indented accoring to the current indentation level.
524 *
525 * @param val Value to print.
526 */
527 public void println(long val) {
528 doIndentation();
529 outs.println(val);
530 indentNext = true;
531 }
532
533 /**
534 * Prints the specified value followed by a new line to std-out and/or file.
535 * Next output will be indented accoring to the current indentation level.
536 *
537 * @param val Value to print.
538 */
539 public void println(float val) {
540 doIndentation();
541 outs.println(val);
542 indentNext = true;
543 }
544
545 /**
546 * Prints the specified value followed by a new line to std-out and/or file.
547 * Next output will be indented accoring to the current indentation level.
548 *
549 * @param val Value to print.
550 */
551 public void println(double val) {
552 doIndentation();
553 outs.println(val);
554 indentNext = true;
555 }
556
557 } // public class ScreenFileLogger