001    // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 
002    // The Molly framework is freely distributable under the terms of an
003    // MIT-style license. For details, see the molly pages web site at:
004    // http://www.mollypages.org/. Use, modify, have fun !
005    
006    package fc.io;
007    
008    import java.text.*;
009    import java.util.*;
010    import java.lang.reflect.*;
011    
012    import fc.util.*;
013    
014    /** 
015    A system wide logging facility. Clearer and more elegant 
016    semantics/nomenclature than <tt>java.util.logging</tt>.
017    <p>
018    Logs messages to some user specified destination(s). Each log has a
019    <b>name</b> and a <b>log level</b>.
020    <p>
021    The names are arbitrary (hopefully descriptive) names, per your liking. Many
022    different logs can be created and then later retrieved by their
023    name as needed.
024    <p>
025    The levels provided by this class have the order:
026      <blockquote>
027      <tt>
028      OFF <b>&lt;</b> ERROR <b>&lt; </b>WARN <b>&lt;</b> INFO <b>&lt;</b> DEBUG
029      </tt>
030      </blockquote>
031    The above levels are self-explanatory. 
032    <p>
033    Convenience methods with names equal to a <b>level</b> name are provided.
034    So for example, instead of saying:
035      <blockquote>
036      <tt>log(<b>LogLevel.warn</b>, "the message");</tt>
037      </blockquote>
038    one can say:
039      <blockquote>
040      <tt>log.<b>warn</b>("the message");</tt>
041      </blockquote>
042    <p>
043    <div style="border: 1px solid #ccc; padding: 1em;">
044    A little historical quirk. For the debug level <tt>LogLevel.debug</b></tt>,
045    one can say:
046      <blockquote>
047      <pre>
048      <tt>log.<b>debug</b>("the message");</tt> 
049         --<b>or</b>--
050      <tt>log.<b>bug</b>("the message");</tt> 
051      </pre>
052      </blockquote>
053    </div>
054    <p>
055    A default logger of type {@link fc.io.SystemLog} is provided for
056    convenience in this class and can be retrieved by calling the {@link
057    #getDefault()} method.
058    <p>
059    The {@link #closeLog(String)} method is called on all logs
060    at JVM shutdown.
061    <p>
062    <b>Note:</b>To log a full stack traces, pass the string obtained by the
063    {@link IOUtil#throwableToString} method. (by default, an exception simply
064    prints its message but not the full stack trace)
065    <p>
066    <b>Implementation Note</b>: subclasses should implement static
067    <tt>getLog(..)</tt> type methods. These that create, as needed, and
068    return a new log object of that subclass type. These getter methods
069    should be static for convenience. Also, the implementation of the
070    <tt>subclass.getLog(...)</tt> methods in subclasses is expected by
071    convention.
072    <p>
073    Thread Safety: This class <tt>is</tt> Threadsafe and all its methods can
074    be used concurrently.
075    
076    @author hursh jain
077    **/
078    public abstract class Log 
079    {
080    //--static variables--
081    public static final LogLevel OFF  = new LogLevel("OFF",   0);
082    public static final LogLevel ERROR  = new LogLevel("ERROR", 1);
083    public static final LogLevel WARN = new LogLevel("WARN",  2);
084    public static final LogLevel INFO = new LogLevel("INFO",  3);
085    public static final LogLevel DEBUG  = new LogLevel("DEBUG", 4);
086    
087    /**
088    The default level used for new logs. Subclasses should (by default) 
089    create logs using this level.
090    */
091    public static LogLevel DEFAULT_LEVEL = INFO;
092    
093    protected   static final byte[] linesepbytes = 
094                System.getProperty("line.separator").getBytes();
095    
096                //synchronized map needed
097    protected   static          SystemLog   defaultlog;
098    protected   static final    Map         logs = new Hashtable(); 
099    
100    //--instance variables apply to a particular logger
101    protected   String      name;
102    protected   LogLevel    currentLevel;
103    protected   long        startTime;  
104    protected   String      startTimeString; //for toString()
105    protected   boolean     printLevelName          = true;
106    protected   boolean     printTimestamp          = false;
107    protected   boolean     timestampIsRelative     = false;
108    protected   LogLevel    printMethodInfoAtLevel  = DEBUG;
109    
110    //default timestamp data
111    protected   final   SimpleDateFormat df 
112                            = new SimpleDateFormat("MMM dd H:m:s z");
113    protected   final   NumberFormat nf     
114                            = NumberFormat.getNumberInstance();
115    
116    protected   Date        date        = new Date();
117    protected   long        last_time   = date.getTime();
118    protected   String      timestr     = df.format(date);
119    
120    //Add shutdown hook to close all open logs
121    static 
122      {
123      Runtime.getRuntime().addShutdownHook(new Thread("fc.io.Log.Shutdown") {
124        public void run() 
125          {
126          System.out.println("JVM shutdown: fc.io.Log - closing all logs...");
127          Iterator i = logs.values().iterator();
128          while (i.hasNext())
129            ((Log)i.next()).close();
130          System.out.println();     
131          }
132        });
133      }
134    
135    
136    /**
137    Constructs a new Log. Can only be called from subclasses.
138    
139    @param  name      the name for the log -- any arbitrary
140                string denoting a conceptual category, 
141                destination, whatever
142    @param  level     the initial logging level for this log
143    */
144    protected Log(String name, LogLevel level) 
145      {
146      assert name != null : "name was null";
147      assert level != null : "level was null" ;
148      this.name = name;
149      this.currentLevel    = level;   
150      this.startTime       = System.currentTimeMillis();
151      this.startTimeString = new Date(startTime).toString();
152      }
153    
154    
155    /**
156    Returns a {@link SystemLog} with the specified name. If the log does not already
157    exist, creates and returns a new SystemLog with that name.
158    <p>
159    The system log created will have a default destination of <tt>System.err</tt>
160    and a default level of {@link Log#INFO}. 
161    <p>
162    To obtain logs with a different destination, create a SystemLog directly.
163    */
164    public static Log get(String name)
165      {
166      Log log = (Log) logs.get(name);
167      if (log != null)
168        return log;
169        
170      synchronized(logs) {
171        log = new SystemLog(name);
172        logs.put(name, log);  
173        }
174        
175      return log;
176      }
177    
178    
179    /**
180    An alias for method {@link #get(String)}.
181    <p>
182    Returns a {@link SystemLog} with the specified name. If the log does not already
183    exist, creates and returns a new SystemLog with that name.
184    <p>
185    The system log created will have a default destination of <tt>System.err</tt>
186    and a default level of {@link Log#INFO}. 
187    <p>
188    To obtain logs with a different destination, create a SystemLog directly.
189    */
190    public static Log getLog(String name)
191      {
192      return get(name);
193      }
194    
195    
196    /**
197    Convenience method that returns the log named after the <b>package</b> that
198    the specified class belong to. If 2 classes <pre>a.b.Class1</pre> and
199    <pre>a.b.Class2</pre> call this method, they will get the same logger
200    (named <tt>a.b</tt>).
201    <p>
202    If the log does not already exist, creates and returns a new {@link SystemLog} 
203    with that name.
204    
205    @param  c a non-null class
206    */
207    public final static Log get(Class c) 
208      {
209      Argcheck.notnull(c, "class parameter was null");  
210      final Package p = c.getPackage();
211      final String name = (p == null) ? "default_pkg" : p.toString();
212      return get(name);
213      }
214    
215    /**
216    Convenience method that returns a log named after the <b>package</b> that
217    the specified object's class belong to. If 2 objects of class
218    <pre>a.b.Class1</pre> and <pre>a.b.Class2</pre> call this method, they
219    will get the same logger (named <tt>a.b</tt>).
220    <p>
221    If the log does not already exist, creates and returns a new {@link SystemLog} 
222    with that name.
223    
224    @param  obj   a non-null object
225    */
226    public final static Log get(Object obj) 
227      {
228      Argcheck.notnull(obj, "class parameter was null");  
229      return get(obj.getClass().getPackage().toString());
230      }
231    
232    /**
233    Returns the default system log. This system log writes to
234    <tt>System.err</tt> and has it's level set to {@link SystemLog#INFO}. This
235    level can be changed to some other level if desired by invoking {@link
236    setLevel()} on the returned object.
237    */
238    public static SystemLog getDefault()
239      {
240      synchronized (Log.class)
241        {
242        if (defaultlog == null) {
243          defaultlog = new SystemLog(
244            "_defaultlog", System.out, SystemLog.INFO);
245          }
246        }   
247      return defaultlog;
248      }
249    
250    
251    /**
252    Returns an iteration containing level names for this log. The names can be in
253    any order.
254    */
255    public Iterator getLevelNames() 
256      {
257      Class myclass = getClass();
258      Field[] fields = myclass.getDeclaredFields();
259      Field levelfield = null;
260      List l = new ArrayList();
261      for (int n = 0; n < fields.length; n++) 
262        {
263        Field f = fields[n];
264        if (! f.getType().isAssignableFrom(LogLevel.class))
265          continue;
266        l.add(f.getName());
267        }
268      return l.iterator();
269      }
270    
271    /*
272    Manually adds the specified log to the list of all logs. If a log with
273    that name already exists, an <tt>IllegalArgumentException</tt> is thrown.
274    <p> This method is useful when creating a new logging object manaually.
275    So for example:
276      <blockquote>
277      <pre>
278      MyLogClass mylog = new MyLogClass("foo.bar");
279      Log.addLog(mylog)
280      </pre>
281      </blockquote>
282    Contrast that with the more usual:
283      <blockquote>
284      <pre>
285      Log.getLog("foo.bar");
286      </pre>
287      </blockquote>
288    <p>
289    <u>Custom log implementations should always call this method in their
290    constructor to add themselves to the list of all logs.</u>
291    */
292    protected static void addLog(Log log)
293      {
294      synchronized(logs)
295        {
296        if (! logs.containsKey(log.name)) {
297          logs.put(log.name, log);  
298          } 
299        else throw new IllegalArgumentException("Log already exists: " + log);  
300        }
301      }
302    
303    /**
304    Closes and removes the log with the specified name if it exists
305    */
306    public static void closeLog(String name) 
307      {
308      if (logs.containsKey(name)) {
309        Log l = (Log) logs.get(name);
310        l.close();
311        logs.remove(name);
312        }
313      }
314    
315    /**
316    Returns the method name, file number and line number
317    of the calling method. Useful for logging/code tracing.
318    
319    @param  level   the level for which this logging call was invoked.  
320    @param  framenum  the method to examine. This method itself
321              has frame number 0, the calling method
322              has frame number 1, it's parent 2 and so on.
323    **/
324    public final String getDebugContext(LogLevel level, int framenum) 
325      {
326      if (level.intval <  printMethodInfoAtLevel.intval) {
327        return "";
328        }
329        
330      StackTraceElement ste[] = new Exception().getStackTrace();
331      if (framenum >= ste.length)
332        throw new IllegalArgumentException(
333         "framenum [" + framenum 
334         + "] too large. Max number of record in stack = "
335         + (ste.length - 1)); 
336    
337      //get method that called us, we are ste[0]
338      StackTraceElement st = ste[framenum];
339      String file = st.getFileName();
340      int line = st.getLineNumber();
341      String method = st.getMethodName();
342      String threadname = Thread.currentThread().getName();
343      //String classn = st.getClassName();
344      return method + "() [" + file + ":" + line + "/thread:" + threadname + "]";   
345      }
346    
347    
348    /**
349    If set to true, will print the level name before the logging
350    message. For example, if the level is <code>INFO</code>, the 
351    message is <code>foo</code>, then 
352      <blockquote>
353      INFO foo
354      </blockquote>
355    will be printed. 
356    <p>
357    This is set to <tt>true</tt> by default.
358    */
359    public void printLevelName(boolean printName)
360      {
361      printLevelName = printName;
362      }
363    
364    /**
365    Prints a time stamp with every message. By default this
366    is <tt>false</tt>
367    
368    @param  val    specify true to print time stamps, false
369             to not
370    */
371    public void printTimestamp(boolean val) {
372      printTimestamp = val;
373      }
374    
375    /**
376    Prints a relative time stamp with every message. By 
377    default, printing any timestamp is <tt>false</tt>.
378    <b>
379    Timestamps must first be enabled via the {@link printTimestamp} 
380    method before this method can have any effect.
381    </b>
382    
383    @param  val   if true, prints a <b>relative</b> time
384            stamp. An initial timestamp is printed and
385            all succeeding timestamps are second
386            increments from the initial timestamp
387    */
388    public void printRelativeTimestamp(boolean val) {
389      timestampIsRelative = val;
390      last_time = new Date().getTime();
391      }
392    
393    /**
394    By default, method, line and thread information is printed wich each
395    logging statement at the DEBUG level. Other levels print only the log
396    message but skip the method/stack information.
397    <p>
398    This method allows method information to be printed at all levels greater
399    than or equal to the specified level.
400    */
401    public void printMethodInfoAtLevel(LogLevel level) {
402      this.printMethodInfoAtLevel = level;
403      }
404    
405    /** 
406    A default implementation that returns an appropriate timestamp based on the
407    timestamp setttings. <b> Multiple threads must synchronize access to this
408    method </b>. Subclasses should call this method in their logging method
409    implementations in the following way.
410    <blockquote>
411    Suppose the subclass uses an out object as a printwriter. Then
412    <br>
413    <code>
414    <b>...in a synchronized block, typically around the output stream....</b>
415    if (printTimestamp) {
416      out.print(getTS());
417      out.print(" ");
418      }
419    .....
420    </code>
421    </blockquote>
422    Of course subclasses are free to not call this method or 
423    print timestamps in some other fashion.
424    */
425    protected final String getTS()
426      {
427      date = new Date();
428      long now = date.getTime();
429    
430      if (timestampIsRelative)
431        {
432        return nf.format( ((now - last_time) / 1000) ) ;  
433        }
434      else  //non relative ts
435        {
436        if (now - last_time >= 1000) {
437          last_time = now;
438          timestr = df.format(date);
439          }
440        }
441        
442      return timestr;
443      }
444    
445    /**
446    Sets the current logging level for this logger. Each log has a logging
447    level. A message is printed only if the message level is equal to or lower
448    than the current maximum level for that log.
449    <p>
450    Typically, classes that implement a log will define a bunch of static
451    variables of type {@link LogLevel} that list the available levels for that
452    implementation. Clients of a particular log class should use levels
453    defined within only that class.
454    */
455    public void setLevel(LogLevel level) {
456      assert level != null : "specified level was null";
457      currentLevel = level;
458      }
459      
460    /**
461    Sets the level of this log based on a level description. This is
462    convenient for when levels are specified in a configuration file. If the
463    specified name cannot be converted into a level, then no change is made.
464    
465    @param  levelname the level name. For example, 
466              <tt>"info"</tt> would set the level of 
467              this log to <tt>INFO</tt>. The name
468              is case-<b>in</b>sensitive. 
469    */
470    public void setLevel(String levelname) 
471      {
472      if (levelname == null) {
473        warn("specified levelname was null, log level will not be changed");
474        return;
475        }
476        
477      try {
478        Field levelfield = stringToLevel(levelname);
479        if (levelfield == null) {   
480          warn("Specified level", levelname, "is not valid/could not be resolved");
481          return;
482          }
483    
484        Method method = Log.class.getMethod("setLevel", new Class[] { LogLevel.class });
485        //System.out.println("got method="+method);
486        method.invoke(this, new Object[] { levelfield.get(this) });
487        info("New log level for log=[", name ,"] set to: ", currentLevel);
488        }
489      catch (Exception e) {
490        warn(e);
491        }
492      }
493    
494    /*
495    Returns a level field corresponding to the specified case-insensitive
496    string.
497    
498    this is kinda overkill but hey: It takes about 5ms so speed
499    isn't an issue and if we ever add more levels, we won't have to
500    update this method.
501    */
502    protected static Field stringToLevel(String levelname)
503      {
504      Field[] fields = Log.class.getDeclaredFields();
505      Field levelfield = null;
506      for (int n = 0; n < fields.length; n++) 
507        {
508        Field f = fields[n];
509        if (! f.getType().isAssignableFrom(LogLevel.class))
510          continue;
511        if (f.getName().equalsIgnoreCase(levelname))
512          levelfield = f;
513        }
514      return levelfield;
515      }
516      
517    /**
518    Sets the level for all logs whose name <b>contain</b> the specified name.
519    This is convenient when changing log levels for package heirarchies. A
520    empty string (non-null) "" sets the level for all logs.
521    
522    @param  levelname the level name. For example, 
523              <tt>"info"</tt> would set the level of 
524              this log to <tt>INFO</tt>. The name
525              is case-<b>in</b>sensitive. 
526    */
527    public static void setLevelForAll(String name, LogLevel level)
528      {
529      Iterator i = logs.keySet().iterator();
530      while (i.hasNext()) {
531        String logname = (String) i.next();
532        if (logname.contains(logname))  {
533          ((Log)logs.get(logname)).setLevel(level);
534          }
535        }
536      }
537    
538    /**
539    Sets the new default logging level for all new instances of loggers
540    (that are created after this method is invoked).
541    */
542    public static void setDefaultLevel(LogLevel level)
543      {
544      DEFAULT_LEVEL = level;
545      }
546    
547    /**
548    Sets the new default logging level for all new instances of loggers
549    (created after this method is invoked).
550    */
551    public static void setDefaultLevel(String level)
552      {
553      if (level == null) {
554        new Exception("the specified level was null, log level will not be changed").printStackTrace();
555        return;
556        }
557        
558      try {
559        Field levelfield = stringToLevel(level);
560        if (levelfield == null)
561          return;
562          
563        Method method = Log.class.getMethod("setDefaultLevel", new Class[] { LogLevel.class });
564        //System.out.println("got method="+method);
565        method.invoke(null, new Object[] { levelfield.get(null) });
566        }
567      catch (Exception e) {
568        e.printStackTrace();
569        }
570      }
571    
572    /**
573    Returns <tt>true</tt> if the log's current level will allow logging
574    messages at the specified logging level.
575    <p>
576    Implementation Note: If the currentLevel is lesser or equal to the
577    specified level returns true, else false. Subclasses can override this
578    method if needed.
579    
580    @param  level the specified logging level
581    */
582    public boolean canLog(LogLevel level) 
583      {
584      assert level != null : "specified level was null";
585      if (level.intval > currentLevel.intval)
586        return false;
587      return true;
588      }
589    
590    /**
591    Returns the name of this log.
592    */
593    public String getName() {
594      return name;
595      }
596    
597    /**
598    Returns the current level set for this log. Useful when
599    printing out debugging info.
600    */
601    public LogLevel getLevel() {
602      return currentLevel;
603      }
604      
605    public void logSystemInfo() 
606      { 
607      StringBuffer buf = new StringBuffer(1024);
608      Properties p = System.getProperties();
609      Enumeration e = p.propertyNames();
610      while (e.hasMoreElements()) {
611        buf.append(IOUtil.LINE_SEP);
612        String name = (String) e.nextElement();   
613        
614        buf.append(name).append("=");
615        
616        if (name.equals("line.separator"))
617          buf.append(StringUtil.viewableAscii(
618                  p.getProperty(name)));
619        else
620          buf.append(p.getProperty(name));    
621        }
622      buf.append(IOUtil.LINE_SEP);    
623      info(buf.toString());
624      }
625    
626    public String toString()
627      {
628      return name + " [" + getClass().getName() + 
629          "/currentlevel:" + currentLevel.desc + 
630          "/started:" + startTimeString + "]";
631      }
632    
633    //--methods for various levels
634    public final void error(final Object str1) {
635      doLog(ERROR, str1); 
636      }
637      
638    public final void error(final Object str1, final Object str2) {
639      doLog(ERROR, str1, str2); 
640      }
641      
642    public final void error(final Object str1, final Object str2, final Object str3) {
643      doLog(ERROR, str1, str2, str3); 
644      }
645    
646    public final void error(final Object str1, final Object str2, final Object str3, final Object str4) {
647      doLog(ERROR, str1, str2, str3, str4); 
648      }
649    
650    public final void error(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5) {
651      doLog(ERROR, str1, str2, str3, str4, str5); 
652      }
653    
654    public final void error(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6) {
655      doLog(ERROR, str1, str2, str3, str4, str5, str6); 
656      }
657    
658    public final void error(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6, final Object str7) {
659      doLog(ERROR, str1, str2, str3, str4, str5, str6, str7); 
660      }
661    
662    public final void error(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6, final Object str7, Object... args) {
663      doLog(ERROR, str1, str2, str3, str4, str5, str6, str7, args); 
664      }
665      
666    public final void warn(final Object str1) {
667      doLog(WARN, str1);  
668      }
669      
670    public final void warn(final Object str1, final Object str2) {
671      doLog(WARN, str1, str2);  
672      }
673      
674    public final void warn(final Object str1, final Object str2, final Object str3) {
675      doLog(WARN, str1, str2, str3);  
676      }
677    
678    public final void warn(final Object str1, final Object str2, final Object str3, final Object str4) {
679      doLog(WARN, str1, str2, str3, str4);  
680      }
681    
682    public final void warn(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5) {
683      doLog(WARN, str1, str2, str3, str4, str5);  
684      }
685    
686    public final void warn(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6) {
687      doLog(WARN, str1, str2, str3, str4, str5, str6);  
688      }
689    
690    public final void warn(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6, final Object str7) {
691      doLog(WARN, str1, str2, str3, str4, str5, str6, str7);  
692      }
693    
694    public final void warn(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6, final Object str7, Object... args) {
695      doLog(WARN, str1, str2, str3, str4, str5, str6, str7, args);  
696      }
697    
698    public final void info(final Object str1) {
699      doLog(INFO, str1);  
700      }
701      
702    public final void info(final Object str1, final Object str2) {
703      doLog(INFO, str1, str2);  
704      }
705      
706    public final void info(final Object str1, final Object str2, final Object str3) {
707      doLog(INFO, str1, str2, str3);  
708      }
709    
710    public final void info(final Object str1, final Object str2, final Object str3, final Object str4) {
711      doLog(INFO, str1, str2, str3, str4);  
712      }
713      
714    public final void info(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5) {
715      doLog(INFO, str1, str2, str3, str4, str5);  
716      }
717    
718    public final void info(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6) {
719      doLog(INFO, str1, str2, str3, str4, str5, str6);  
720      }
721    
722    public final void info(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6, final Object str7) {
723      doLog(INFO, str1, str2, str3, str4, str5, str6, str7);  
724      }
725    
726    public final void info(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6, final Object str7, Object... args) {
727      doLog(INFO, str1, str2, str3, str4, str5, str6, str7, args);  
728      }
729    
730    public final void debug(final Object str1) {
731      doLog(DEBUG, str1); 
732      }
733    
734    public final void debug(final Object str1, final Object str2) {
735      doLog(DEBUG, str1, str2); 
736      }
737      
738    public final void debug(final Object str1, final Object str2, final Object str3) {
739      doLog(DEBUG, str1, str2, str3); 
740      }
741    
742    public final void debug(final Object str1, final Object str2, final Object str3, final Object str4) {
743      doLog(DEBUG, str1, str2, str3, str4); 
744      }
745    
746    public final void debug(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5) {
747      doLog(DEBUG, str1, str2, str3, str4, str5); 
748      }
749    
750    public final void debug(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6) {
751      doLog(DEBUG, str1, str2, str3, str4, str5, str6); 
752      }
753    
754    public final void debug(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6, final Object str7) {
755      doLog(DEBUG, str1, str2, str3, str4, str5, str6, str7); 
756      }
757    
758    public final void debug(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6, final Object str7, Object... args) {
759      doLog(DEBUG, str1, str2, str3, str4, str5, str6, str7, args); 
760      }
761    
762    public final void bug(final Object str1) {
763      doLog(DEBUG, str1); 
764      }
765    
766    public final void bug(final Object str1, final Object str2) {
767      doLog(DEBUG, str1, str2); 
768      }
769      
770    public final void bug(final Object str1, final Object str2, final Object str3) {
771      doLog(DEBUG, str1, str2, str3); 
772      }
773    
774    public final void bug(final Object str1, final Object str2, final Object str3, final Object str4) {
775      doLog(DEBUG, str1, str2, str3, str4); 
776      }
777    
778    public final void bug(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5) {
779      doLog(DEBUG, str1, str2, str3, str4, str5); 
780      }
781    
782    public final void bug(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6) {
783      doLog(DEBUG, str1, str2, str3, str4, str5, str6); 
784      }
785    
786    public final void bug(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6, final Object str7) {
787      doLog(DEBUG, str1, str2, str3, str4, str5, str6, str7); 
788      }
789    
790    public final void bug(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6, final Object str7, Object... args) {
791      doLog(DEBUG, str1, str2, str3, str4, str5, str6, str7, args); 
792      }
793      
794    final void doLog(final LogLevel level, final Object str1) 
795      {
796      if (level.intval > currentLevel.intval)
797        return;
798      
799      log(level, getDebugContext(level, 3), str1);
800      }
801    
802    final void doLog(final LogLevel level, final Object str1, final Object str2) 
803      {
804      if (level.intval > currentLevel.intval)
805        return;
806      
807      log(level, getDebugContext(level, 3), str1, str2);
808      }
809    
810    final void doLog(final LogLevel level, final Object str1, 
811             final Object str2, final Object str3) 
812      {
813      if (level.intval > currentLevel.intval)
814        return;
815      
816      log(level, getDebugContext(level, 3), str1, str2, str3);
817      }
818    
819    final void doLog(final LogLevel level, final Object str1, final Object str2, 
820             final Object str3, final Object str4) 
821      {
822      if (level.intval > currentLevel.intval)
823        return;
824      
825      log(level, getDebugContext(level, 3), str1, str2, str3, str4);
826      }
827    
828    final void doLog(final LogLevel level, final Object str1, final Object str2, 
829             final Object str3, final Object str4, final Object str5) 
830      {
831      if (level.intval > currentLevel.intval)
832        return;
833      
834      log(level, getDebugContext(level, 3), str1, str2, str3, str4, str5);
835      }
836    
837    final void doLog(final LogLevel level, final Object str1, final Object str2, 
838             final Object str3, final Object str4, final Object str5, 
839             final Object str6) 
840      {
841      if (level.intval > currentLevel.intval)
842        return;
843      
844      log(level, getDebugContext(level, 3), str1, str2, str3, str4, str5, str6);
845      }
846    
847    final void doLog(final LogLevel level, final Object str1, final Object str2, 
848             final Object str3, final Object str4, final Object str5, 
849             final Object str6, final Object str7) 
850      {
851      if (level.intval > currentLevel.intval)
852        return;
853      
854      log(level, getDebugContext(level, 3), str1, str2, str3, str4, str5, str6, str7);
855      }
856    
857    final void doLog(final LogLevel level, final Object str1, final Object str2, 
858             final Object str3, final Object str4, final Object str5, 
859             final Object str6, final Object str7, Object... args) 
860      {
861      if (level.intval > currentLevel.intval)
862        return;
863      
864      log(level, getDebugContext(level, 3), str1, str2, str3, str4, str5, str6, str7, args);
865      }
866    
867    
868    //--abstract methods--
869    
870    public abstract void close();
871    
872    
873    /**
874    @param  level the current log level. This can be logged
875            as well.
876    @param  str1  unless overridden in a subclass, this is the
877            value returned by {@link getDebugContext} and
878            is generated automatically by the warn(), 
879            info(), debug() etc., methods
880    */
881    public abstract void log(LogLevel level, Object str1); 
882    
883    /**
884    @param  level the current log level. This can be logged
885            as well.
886    @param  str1  unless overridden in a subclass, this is the
887            value returned by {@link getDebugContext} and
888            is generated automatically by the warn(), 
889            info(), debug() etc., methods
890    @param  str2_onwards  
891            some arbitrary object
892    */
893    public abstract void log(LogLevel level, final Object str1, final Object str2);
894    
895    /**
896    @param  level the current log level. This can be logged
897            as well.
898    @param  str1  unless overridden in a subclass, this is the
899            value returned by {@link getDebugContext} and
900            is generated automatically by the warn(), 
901            info(), debug() etc., methods
902    @param  str2_onwards  
903            some arbitrary object
904    */
905    public abstract void log(LogLevel level, final Object str1, final Object str2, final Object str3);
906    
907    /**
908    @param  level the current log level. This can be logged
909            as well.
910    @param  str1  unless overridden in a subclass, this is the
911            value returned by {@link getDebugContext} and
912            is generated automatically by the warn(), 
913            info(), debug() etc., methods
914    @param  str2_onwards  
915            some arbitrary object
916    */
917    public abstract void log(LogLevel level, final Object str1, final Object str2, final Object str3, final Object str4);
918    
919    /**
920    @param  level the current log level. This can be logged
921            as well.
922    @param  str1  unless overridden in a subclass, this is the
923            value returned by {@link getDebugContext} and
924            is generated automatically by the warn(), 
925            info(), debug() etc., methods
926    @param  str2_onwards  
927            some arbitrary object
928    */
929    public abstract void log(LogLevel level, final Object str1, final Object str2, final Object str3, final Object str4, final Object str5);
930    
931    /**
932    @param  level the current log level. This can be logged
933            as well.
934    @param  str1  unless overridden in a subclass, this is the
935            value returned by {@link getDebugContext} and
936            is generated automatically by the warn(), 
937            info(), debug() etc., methods
938    @param  str2_onwards  
939            some arbitrary object
940    */
941    public abstract void log(LogLevel level, final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6);
942    
943    /**
944    @param  level the current log level. This can be logged
945            as well.
946    @param  str1  unless overridden in a subclass, this is the
947            value returned by {@link getDebugContext} and
948            is generated automatically by the warn(), 
949            info(), debug() etc., methods
950    @param  str2_onwards  
951            some arbitrary object
952    */
953    public abstract void log(LogLevel level, final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6, final Object str7);
954    
955    /**
956    @param  level the current log level. This can be logged
957            as well.
958    @param  str1  unless overridden in a subclass, this is the
959            value returned by {@link getDebugContext} and
960            is generated automatically by the warn(), 
961            info(), debug() etc., methods
962    @param  str2_onwards  
963            some arbitrary object
964    */
965    public abstract void log(LogLevel level, final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6, final Object str7, Object str8);
966    
967    /**
968    @param  level the current log level. This can be logged
969            as well.
970    @param  str1  unless overridden in a subclass, this is the
971            value returned by {@link getDebugContext} and
972            is generated automatically by the warn(), 
973            info(), debug() etc., methods
974    @param  str2_onwards  
975            some arbitrary object
976    */
977    public abstract void log(LogLevel level, final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6, final Object str7, Object str8, Object... args);
978    }          //~class Log
979    
980