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