001/*
002 *  $Source: v:/cvsroot/open/projects/WebARTS/ca/bc/webarts/tools/Log.java,v $
003 *  $Revision: 1295 $  $Date: 2019-04-18 18:05:01 -0700 (Thu, 18 Apr 2019) $  $Locker:  $
004 *  Copyright (C) 2001 WebARTS Design,
005 *  North Vancouver Canada. All Rights Reserved.
006 *
007 *  Written by Tom Gutwin - WebARTS Design.
008 *  http://www..webarts.bc.ca
009 *
010 *  This program is free software; you can redistribute it and/or modify
011 *  it under the terms of the GNU General Public License as published by
012 *  the Free Software Foundation; either version 2 of the License, or
013 *  (at your option) any later version.
014 *
015 *  This program is distributed in the hope that it will be useful,
016 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
017 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
018 *  GNU General Public License for more details.
019 *
020 *  You should have received a copy of the GNU General Public License
021 *  along with this program; if not, write to the Free Software
022 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
023 */
024/*
025Here is the revision log
026------------------------
027$Log: Log.java,v $
028Revision 1.10  2002/04/04 23:04:26  anonymous
029Added a few extra null checks to ensure a Log gets created.
030
031Revision 1.9  2002/04/03 00:01:42  anonymous
032Another fix to the logLevel check.
033
034Revision 1.8  2002/04/02 23:31:36  anonymous
035Fixed the logLevel_ compare on the debug methods.
036
037Revision 1.7  2002/03/14 23:09:52  tgutwin
038
039Added/Updated Many Comments.
040Added set and get logLeve and LogLevelString.
041logEntry is now private.
042Added the checking for logLevel on log methods.
043
044Revision 1.6  2001/12/04 06:43:49  tgutwin
045
046Added a null check into the log entry method.
047
048Revision 1.5  2001/11/21 03:59:11  tgutwin
049
050Added a get instance() method that ssimply returns the log_.
051It is to be used when you don't want to create a log.
052
053Revision 1.4  2001/10/08 20:28:26  tgutwin
054
055Made the Log Static ( only one instance Used. Plus added an initial std out message as to where the log is going.
056
057Revision 1.3  2001/10/07 23:36:33  tgutwin
058
059Created a single Log Output stream that different sources are now mapped into.
060ie ... FileStream StdOut, or pip Stream.
061
062*/
063
064package ca.bc.webarts.tools;
065
066import java.io.File;
067import java.io.FileOutputStream;
068import java.io.FileWriter;
069import java.io.IOException;
070import java.io.OutputStream;
071import java.io.PrintStream;
072import java.lang.Exception;
073import java.util.Calendar;
074import java.util.Stack;
075
076/**
077 *  A Simple Log class that can be set to output to a file, the Console or any
078 *  specified OutputStream. It provides a few simple log entry methods with
079 *  method entry/exit tracking.<br />It is a Singleton so that all classes using it log to the same instance/OutputStream.
080 * Use one of the Log.createLog(...) methods to gat a handel to the current instance.
081 *
082 * @author     Tom Gutwin P.Eng
083 * @created    September 17, 2001
084 */
085public class Log
086{
087  private static boolean instantiated_ = false;
088  private static Log log_ = null;
089  /**
090   *  Constant Specifying NO logging will occur.
091   */
092  public final static short NONE = 0;
093  /**
094   *  Constant Specifying minimal logging
095   *  will occur (only Method entry and exit).
096   */
097  public final static short QUIET = 1;
098  /**
099   *  Constant Specifying only Major Log events logging will occur.
100   */
101  public final static short MAJOR = 2;
102  /**
103   *  Constant Specifying Major and Minor Log events logging will occur.
104   */
105  public final static short MINOR = 3;
106  /**
107   *  Constant Specifying Major Minor and Debug Log events logging will occur.
108   */
109  public final static short DEBUG = 4;
110  /**
111   *  Constant Specifying ALL log event logging will occur.
112   */
113  public final static short FULL = 5;
114
115  /**
116   *  Constant Specifying that the logging output will go to System.out.
117   */
118  public final static short CONSOLE = 50;
119  /**
120   *  Constant Specifying that the logging output will go to a spec'd file.
121   */
122  public final static short FILE = 51;
123  /**
124   *  Constant Specifying that the logging output will go to a
125   *  spec'd OutputStream.
126   */
127  public final static short PIPE = 52;
128
129  /**
130   *  The Standard OutputStream goes to System.out.
131   */
132  private final static PrintStream STD_OUT = System.out;
133
134  /**
135   *  The name of the current log file. Default is logfile.txt.
136   */
137  static private String    logFileName_ = "logfile.txt";
138  /**
139   *  Class field that is used to pad the log messages based on the level of the method being logged.
140   */
141  static private String    msgSpacing_ = "";
142  /**
143   * Class field used for the PIPed output stream being used.
144   */
145  static private OutputStream userOut_ = STD_OUT;
146  /**
147   *  The stream that ALL logging ends up getting directed to.
148   */
149  static private PrintStream logOut_ = null;
150  /**
151   *  Description of the Field
152   */
153  static private File      logFile_ = null;
154  /**
155   *  Description of the Field
156   */
157  static private FileWriter logFileWriter_ = null;
158  /**
159   *  Description of the Field
160   */
161  static private short     logLevel_ = MAJOR;
162  /**
163   *  Description of the Field
164   */
165  static private short     outSpot_ = CONSOLE;
166  /**
167   *  Description of the Field
168   */
169  static private Calendar  calendar_ = Calendar.getInstance();
170
171  /**
172   *  A stack of the method call history.
173   */
174  static private Stack     currentMethod_ = new Stack();
175
176
177  /**
178   *  Default Constructor for the Log object. The ouput location is Console and
179   *  the log level is Major.
180   */
181  private Log()
182  {
183    outSpot_ = CONSOLE;
184    logLevel_ = MAJOR;
185    instantiated_ = true;
186    log_ = this;
187    initLogOut();
188  }
189
190
191  /**
192   *  Constructor for the Log object that sends the output to the Console at the
193   *  specified Log Level.
194   *
195   * @param  logLevel  The desired Logging level (must be one of the Constants
196   *                   spec'd in this class).
197   */
198  private Log(short logLevel)
199  {
200    outSpot_ = CONSOLE;
201    logLevel_ = logLevel;
202    instantiated_ = true;
203    log_ = this;
204    initLogOut();
205  }
206
207
208  /**
209   *  Constructor for the Log object that sends the output to an OutputStream
210   *  Pipe at the specified Log Level.
211   *
212   * @param  logLevel  The desired Logging level (must be one of the Constants
213   *                   spec'd in this class).
214   * @param  o         The OutputStream to Pipe the logging to.
215   */
216  private Log(short logLevel, OutputStream o)
217  {
218    outSpot_ = PIPE;
219    logLevel_ = logLevel;
220    userOut_ = o;
221    instantiated_ = true;
222    log_ = this;
223    initLogOut();
224  }
225
226
227  /**
228   *  Constructor for the Log object that sends the output to a File
229   *  at the specified Log Level.
230   *
231   * @param  logLevel  The desired Logging level (must be one of the Constants
232   *                   spec'd in this class).
233   * @param  logFile   The File to send the Logging to.
234   */
235  private Log(short logLevel, File logFile)
236  {
237    outSpot_ = FILE;
238    logLevel_ = logLevel;
239    if (!setLogFile(logFile))
240    {
241      outSpot_ = CONSOLE;
242      initLogOut();
243      System.out.println("Error Creating Logfile.");
244    }
245    else
246    {
247      instantiated_ = true;
248      log_ = this;
249      initLogOut();
250      //System.out.println("Creating Log: "+logFileName_+" at level "+
251        //getLogLevelString());
252    }
253  }
254
255
256  /**
257   *  Constructor for the Log object that sends the output to a File with the
258   *  spec'd filename at the specified Log Level.
259   *
260   * @param  logLevel  The desired Logging level (must be one of the Constants
261   *                   spec'd in this class).
262   * @param  logName   The filename for the File to send Logging output to.
263   */
264  private Log(short logLevel, String logName)
265  {
266    outSpot_ = FILE;
267    logLevel_ = logLevel;
268
269    if (!setLogFile(logName))
270    {
271      outSpot_ = CONSOLE;
272      initLogOut();
273      System.out.println("Error Creating Logfile.");
274    }
275    else
276    {
277      instantiated_ = true;
278      log_ = this;
279      initLogOut();
280      //System.out.println("In Log(): Creating Log: "+logFileName_+" at level "+
281        //getLogLevelString());
282    }
283  }
284
285
286  /**
287   * Singleton method to get the log instance going to a File.
288   **/
289  public static Log createLog(short logLevel, File logFile)
290  {
291    Log retVal = null;
292    if (instantiated_ && log_ != null)
293    {
294      retVal = log_;
295    }
296    else
297    {
298      retVal = new Log(logLevel, logFile);
299      log_ = retVal;
300      logEntry("Log.createLog", "Creating Log: "+logFileName_+" at level "+
301        getLogLevelString());
302//System.out.println("Log.createLog(short logLevel, File): File="+logFile.toString());
303    }
304    return retVal;
305  }
306
307
308  /**
309   * Singleton method to get the log instance going to an OutputStream.
310   **/
311  public static Log createLog(short logLevel, OutputStream stream)
312  {
313    Log retVal = null;
314    if (instantiated_ && log_ != null)
315    {
316      retVal = log_;
317    }
318    else
319    {
320      retVal = new Log(logLevel, stream);
321      log_ = retVal;
322      logEntry("Log.createLog", "Creating Log: "+logFileName_+" at level "+
323        getLogLevelString());
324//System.out.println("Log.createLog(short logLevel, OutputStream): OutputStream="+stream);
325    }
326    return retVal;
327  }
328
329
330  /**
331   * Singleton method to get the log instance going to a File.
332   **/
333  public static Log createLog(short logLevel, String fName)
334  {
335    //System.out.println("Into createLog");
336    //System.out.println("instantiated_: "+instantiated_);
337    //System.out.println("log_ != null: "+(log_ != null));
338    Log retVal = null;
339    if (instantiated_ && log_ != null)
340      retVal = log_;
341    else
342    {
343      retVal = new Log(logLevel, fName);
344      log_ = retVal;
345      logEntry("Log.createLog", "Creating Log: "+fName+" at level "+
346        getLogLevelString());
347      //System.out.println("Creating Log: "+fName+" at level "+
348        //getLogLevelString());
349    }
350    return retVal;
351  }
352
353
354  /**
355   * Singleton method to get the log instance going to stdout.
356   **/
357  public static Log createLog(short logLevel)
358  {
359    Log retVal = null;
360    if (instantiated_ && log_ != null)
361      retVal = log_;
362    else
363    {
364      retVal = new Log(logLevel);
365      log_ = retVal;
366      logEntry("Log.createLog", "Creating Log: "+logFileName_+" at level "+
367        getLogLevelString());
368//System.out.println("Log.createLog(short logLevel)");
369    }
370    return retVal;
371  }
372
373  /**
374   * Singleton method to get the log instance NULL if not init
375   * yet with a createLog call.
376   **/
377  public static Log getInstance()
378  {
379    if (log_!=null)
380      return log_;
381    else
382      return createLog(MAJOR);
383  }
384
385
386  public static synchronized boolean alreadyInstantiated()
387  {
388    return instantiated_;
389  }
390
391
392  /**
393   * Sets the current Logging Level.
394   *
395   * @return true if it was set false if NOT set due to level out out bounds.
396   **/
397  public static synchronized boolean setLogLevel(short level)
398  {
399    boolean retVal = false;
400    if (level >= NONE && level <= FULL)
401    {
402      retVal = true;
403      logLevel_ = level;
404    }
405    return retVal;
406  }
407
408
409  public static short getLogLevel()
410  {
411    return logLevel_;
412  }
413
414
415  public static String getLogLevelString()
416  {
417    String retVal = "";
418    switch (logLevel_)
419    {
420      case NONE:
421        retVal="NONE";
422        break;
423      case QUIET:
424        retVal="QUIET";
425        break;
426      case MAJOR:
427        retVal="MAJOR";
428        break;
429      case MINOR:
430        retVal="MINOR";
431        break;
432      case DEBUG:
433        retVal="DEBUG";
434        break;
435      case FULL:
436        retVal="FULL";
437        break;
438    }
439    return retVal;
440  }
441
442
443  /**
444   *  Creates a File object based on the spec'd filename and then inits the
445   *  logFile_ attribute.
446   *
447   * @param  fName  The filename to use for the new logFile_.
448   * @return        true if successfully created and init.
449   */
450  static public boolean setLogFile(String fName)
451  {
452    boolean retVal = true;
453    String newFname = "";
454    String tempStr = "";
455    for (int i = 0; i < fName.length(); i++)
456    {
457      tempStr = String.valueOf(fName.charAt(i));
458      if (!(" ".equals(tempStr)) && !(":".equals(tempStr)))
459      {
460        newFname += tempStr;
461      }
462      else
463      {
464        newFname += "_";
465      }
466    }
467    try
468    {
469      logFile_ = new File(newFname);
470      logFileName_ = newFname;
471    }
472    catch (NullPointerException nullEx)
473    {
474      STD_OUT.println("Error Creating Logfile1: \n  " + nullEx.getMessage());
475      retVal = false;
476    }
477
478    return retVal;
479  }
480
481
482  /**
483   *  Sets the logFile_ attribute to the spec'd File.
484   *
485   * @param  logFile  The File object to setr to the logFile_.
486   * @return          true if successfully init.
487   */
488  static public boolean setLogFile(File logFile)
489  {
490    boolean retVal = true;
491
492    if (logFile != null && logFile.exists() && logFile.canRead())
493    {
494      logFile_ = logFile;
495      logFileName_ = logFile.getName();
496    }
497    else
498    {
499      STD_OUT.println("Error Creating Logfile from Specified FILE. ");
500      retVal = false;
501    }
502
503    return retVal;
504  }
505
506
507  /**
508   *  Creates a timestamp for the current time in the form of
509   *  'hour + "-" + min + "-" + sec + "-" + millis'.
510   *
511   * @return    The CurrentTimeStamp value.
512   */
513  static public String createCurrentTimeStamp()
514  {
515    String value = "";
516    calendar_ = Calendar.getInstance();
517    int currMillis = calendar_.get(calendar_.MILLISECOND);
518    String millis = String.valueOf(currMillis);
519    if (currMillis < 10)
520    {
521      millis = "00" + currMillis;
522    }
523    else if (currMillis < 100)
524    {
525      millis = "0" + currMillis;
526    }
527    int currSec = calendar_.get(calendar_.SECOND);
528    String sec = String.valueOf(currSec);
529    if (currSec < 10)
530    {
531      sec = "0" + currSec;
532    }
533    int currMin = calendar_.get(calendar_.MINUTE);
534    String min = String.valueOf(currMin);
535    if (currMin < 10)
536    {
537      min = "0" + currMin;
538    }
539    int currHr = calendar_.get(calendar_.HOUR);
540    String hour = String.valueOf(currHr);
541    if (currHr < 10)
542    {
543      hour = "0" + currHr;
544    }
545
546    return (String)hour + "-" + min + "-" + sec + "-" + millis;
547  }
548
549
550  /**
551   *  Cleanup method that closes any open Files.
552   */
553  static public void close()
554  {
555    try
556    {
557      if (logFileWriter_ != null)
558      {
559        STD_OUT.println("Closing Logfile: " + logFile_.getAbsolutePath());
560        logFileWriter_.close();
561      }
562    }
563    catch (IOException ioEx)
564    {
565      STD_OUT.println("Error Closing Logfile: \n  " + ioEx.getMessage());
566    }
567  }
568
569
570  /**
571   *  Generic uncategorized log entry.
572   *
573   * @param  value  The String to dump into the Log.
574   */
575  static private void logEntry(String value)
576  {
577    if (logOut_ != null && msgSpacing_!=null && !msgSpacing_.equals("") )
578    {
579      value = createCurrentTimeStamp() + ":" + msgSpacing_ + value;
580
581      /* if (outSpot_ == FILE)
582      {
583        value += "\n";
584      }
585      */
586      logOut_.println(value);
587    }
588  }
589
590
591  /**
592   *  Generic uncategorized log entry.
593   *
594   * @param  value  The String to dump into the Log.
595   */
596  static private void logEntry(String value, Exception ex)
597  {
598    if (logOut_ != null && msgSpacing_!=null && !msgSpacing_.equals("") )
599    {
600      value = createCurrentTimeStamp() + ":"+logLevel_ +" "+ msgSpacing_ + value;
601
602     /* if (outSpot_ == FILE)
603      {
604        value += "\n";
605      }
606  */
607      logOut_.println(value);
608      ex.printStackTrace(logOut_);
609    }
610  }
611
612
613  /**
614   *  A log entry method
615   *
616   * @param  methodName  Description of Parameter
617   * @param  value       The String to dump into the Log.
618   */
619  static private void logEntry(String methodName, String value)
620  {
621    logEntry(value);
622  }
623
624
625  /**
626   *  Returns the calling classes name by going through the Stacktrace.
627   @return the calling methods name.
628   */
629  public static String findCurrentClassName()
630  {
631   Throwable t = new Throwable();
632   StackTraceElement[] es = t.getStackTrace();
633   StackTraceElement e = es[2];
634   return e.getClassName();
635   //return e.getLineNumber();
636   //return e.getMethodName();
637  }
638
639
640  /**
641   *  Returns the calling methods name by going through the Stacktrace.
642   @return the calling methods name.
643   */
644  public static String findCurrentMethodName()
645  {
646   Throwable t = new Throwable();
647   StackTraceElement[] es = t.getStackTrace();
648   StackTraceElement e = es[2];
649   //return e.getClassName();
650   //return e.getLineNumber();
651   return e.getMethodName();
652  }
653
654
655  /**
656   *  Generic uncategorized log entry.
657   *
658   * @param  value  The String to dump into the Log.
659   */
660  static private String getCurrentMethodName()
661  {
662    String retVal = "!";
663    if (!currentMethod_.empty())
664    {
665      retVal = (String) currentMethod_.peek();
666    }
667    return retVal;
668  }
669
670
671  /**
672   *  Logs an entry into a method with a simple log message. It also keeps
673   *  track of the entry so it can manage tabbing/spacing of log entries.
674   *
675   * @param  methodName  The method being entered.
676   */
677  static public void startMethod() { startMethod(findCurrentClassName()+"."+findCurrentMethodName());}
678  /**
679   *  Logs an entry into a method with a simple log message. It also keeps
680   *  track of the entry so it can manage tabbing/spacing of log entries.
681   *
682   * @param  methodName  The method being entered.
683   */
684  static public void startMethod(String methodName)
685  {
686    if (logLevel_>=DEBUG)
687    {
688      currentMethod_.push(methodName);
689      logEntry("Entering: " + methodName);
690      msgSpacing_ += "  ";
691    }
692  }
693
694
695  /**
696   *  Logs the exit from a method with a simple log message. It also keeps
697   *  track of the exit so it can manage tabbing/spacing of log entries.
698   *
699   * @param  methodName  The method being exited.
700   */
701  static public void endMethod(String methodName)
702  {
703    if (logLevel_>=DEBUG)
704    {
705      if (currentMethod_.size() > 0)
706      {
707        currentMethod_.pop();
708        if (msgSpacing_.length() > 1)
709        {
710          msgSpacing_ = msgSpacing_.substring(0, msgSpacing_.length() - 2);
711        }
712      }
713      logEntry("Exiting : " + methodName);
714    }
715  }
716
717
718  /**
719   *  Logs the exit from the last entered method with a simple log message.
720   *  It also keeps track of the exit so it can manage tabbing/spacing
721   *  of log entries.
722   */
723  static public void endMethod()
724  {
725    if (logLevel_>=DEBUG)
726    {
727      if (msgSpacing_.length() > 1)
728      {
729        msgSpacing_ = msgSpacing_.substring(0, msgSpacing_.length() - 2);
730      }
731      if (currentMethod_.size() > 0)
732        logEntry("Exiting : " + currentMethod_.pop());
733      else
734        logEntry("Exiting : " );
735    }
736  }
737
738
739  /**
740   *  Logs a Major Log event with the spec'd method name prefix.
741   *
742   * @param  methodName  The methodname to prefix the log message with.
743   * @param  value       The String to dump into the Log.
744   */
745  static public void major(String methodName, String value)
746  {
747    if (logLevel_>=MAJOR)
748      logEntry("Major: " + methodName + ": " + value);
749  }
750
751
752  /**
753   *  Logs a Minor level message prefixed with the spec'd methodname.
754   *
755   * @param  methodName  The methodname to prefix the log message with.
756   * @param  value       The String to dump into the Log.
757   */
758  static public void minor(String methodName, String value)
759  {
760    if (logLevel_>=MINOR)
761      logEntry("Minor: " + methodName + ": " + value);
762  }
763
764
765  /**
766   *  Logs a Debug level message prefixed with the spec'd methodname.
767   *
768   * @param  methodName  The methodname to prefix the log message with.
769   * @param  value       The String to dump into the Log.
770   */
771  static public void debug(String methodName, String value)
772  {
773    if (logLevel_>=DEBUG)
774      logEntry("--> Debug: " + methodName + ": " + value);
775  }
776
777
778  /**
779   *  Logs a Debug level message prefixed with the current methodname.
780   *
781   * @param  value  The String to dump into the Log.
782   */
783  static public void debug(String value)
784  {
785    if (logLevel_>=DEBUG)
786      logEntry("--> Debug: " + getCurrentMethodName() + ": " + value);
787  }
788
789
790  /**
791   *  Logs a Major level message prefixed with the current methodname.
792   *
793   * @param  value  The String to dump into the Log.
794   */
795  static public void major(String value)
796  {
797    if (logLevel_>=MAJOR)
798    logEntry("Major: " + getCurrentMethodName() + ": " + value);
799  }
800
801
802  /**
803   *  Logs a Major level message prefixed with the current methodname.
804   *
805   * @param  value  The String to dump into the Log.
806   * @param  ex  An exception that will dump its stacktrace into the log.
807   */
808  static public void major(String value, Exception ex)
809  {
810    if (logLevel_>=MAJOR)
811    logEntry("Major: " + getCurrentMethodName() + ": " + value, ex);
812  }
813
814
815  /**
816   *  Logs a minor level message prefixed with the current methodname.
817   *
818   * @param  value  The String to dump into the Log.
819   */
820  static public void minor(String value)
821  {
822    if (logLevel_>=MINOR)
823    logEntry("Minor: " + getCurrentMethodName() + ": " + value);
824  }
825
826
827  /**
828   *  Logs a minor level message prefixed with the current methodname.
829   *
830   * @param  value  The String to dump into the Log.
831   * @param  ex  An exception that will dump its stacktrace into the log.
832   */
833  static public void minor(String value, Exception ex)
834  {
835    if (logLevel_>=MINOR)
836      logEntry("Minor: " + getCurrentMethodName() + ": " + value, ex);
837  }
838
839
840  /**
841   *  Initializes the logOut_ Stream based on the outSpot specifired by the user.
842   */
843  static private void initLogOut()
844  {
845    switch (outSpot_)
846    {
847      case FILE:
848        if (logFile_ != null)
849        {
850          try
851          {
852            logOut_ = new PrintStream(new FileOutputStream(logFile_), true);
853          }
854          catch (IOException ioEx)
855          {
856            STD_OUT.println("Error Creating Logfile:\n  " + ioEx.getMessage());
857            outSpot_ = CONSOLE;
858            initLogOut();
859          }
860        }
861        else
862        {
863          outSpot_ = CONSOLE;
864          initLogOut();
865        }
866        break;
867      case PIPE:
868        logOut_ = new PrintStream(userOut_, true);
869        break;
870      default: // default is CONSOLE
871        logOut_ = new PrintStream(STD_OUT, true);
872    }
873  }
874}
875