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(p.getProperty(name)));
618          }
619        else{
620          buf.append(p.getProperty(name));
621          }
622        }
623      buf.append(IOUtil.LINE_SEP);    
624      info(buf.toString());
625      }
626    
627    public String toString()
628      {
629      return name + " [" + getClass().getName() + 
630          "/currentlevel:" + currentLevel.desc + 
631          "/started:" + startTimeString + "]";
632      }
633    
634    //--methods for various levels
635    public final void error(final Object str1) {
636      doLog(ERROR, str1); 
637      }
638      
639    public final void error(final Object str1, final Object str2) {
640      doLog(ERROR, str1, str2); 
641      }
642      
643    public final void error(final Object str1, final Object str2, final Object str3) {
644      doLog(ERROR, str1, str2, str3); 
645      }
646    
647    public final void error(final Object str1, final Object str2, final Object str3, final Object str4) {
648      doLog(ERROR, str1, str2, str3, str4); 
649      }
650    
651    public final void error(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5) {
652      doLog(ERROR, str1, str2, str3, str4, str5); 
653      }
654    
655    public final void error(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6) {
656      doLog(ERROR, str1, str2, str3, str4, str5, str6); 
657      }
658    
659    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) {
660      doLog(ERROR, str1, str2, str3, str4, str5, str6, str7); 
661      }
662    
663    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) {
664      doLog(ERROR, str1, str2, str3, str4, str5, str6, str7, args); 
665      }
666      
667    public final void warn(final Object str1) {
668      doLog(WARN, str1);  
669      }
670      
671    public final void warn(final Object str1, final Object str2) {
672      doLog(WARN, str1, str2);  
673      }
674      
675    public final void warn(final Object str1, final Object str2, final Object str3) {
676      doLog(WARN, str1, str2, str3);  
677      }
678    
679    public final void warn(final Object str1, final Object str2, final Object str3, final Object str4) {
680      doLog(WARN, str1, str2, str3, str4);  
681      }
682    
683    public final void warn(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5) {
684      doLog(WARN, str1, str2, str3, str4, str5);  
685      }
686    
687    public final void warn(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6) {
688      doLog(WARN, str1, str2, str3, str4, str5, str6);  
689      }
690    
691    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) {
692      doLog(WARN, str1, str2, str3, str4, str5, str6, str7);  
693      }
694    
695    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) {
696      doLog(WARN, str1, str2, str3, str4, str5, str6, str7, args);  
697      }
698    
699    public final void info(final Object str1) {
700      doLog(INFO, str1);  
701      }
702      
703    public final void info(final Object str1, final Object str2) {
704      doLog(INFO, str1, str2);  
705      }
706      
707    public final void info(final Object str1, final Object str2, final Object str3) {
708      doLog(INFO, str1, str2, str3);  
709      }
710    
711    public final void info(final Object str1, final Object str2, final Object str3, final Object str4) {
712      doLog(INFO, str1, str2, str3, str4);  
713      }
714      
715    public final void info(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5) {
716      doLog(INFO, str1, str2, str3, str4, str5);  
717      }
718    
719    public final void info(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6) {
720      doLog(INFO, str1, str2, str3, str4, str5, str6);  
721      }
722    
723    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) {
724      doLog(INFO, str1, str2, str3, str4, str5, str6, str7);  
725      }
726    
727    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) {
728      doLog(INFO, str1, str2, str3, str4, str5, str6, str7, args);  
729      }
730    
731    public final void debug(final Object str1) {
732      doLog(DEBUG, str1); 
733      }
734    
735    public final void debug(final Object str1, final Object str2) {
736      doLog(DEBUG, str1, str2); 
737      }
738      
739    public final void debug(final Object str1, final Object str2, final Object str3) {
740      doLog(DEBUG, str1, str2, str3); 
741      }
742    
743    public final void debug(final Object str1, final Object str2, final Object str3, final Object str4) {
744      doLog(DEBUG, str1, str2, str3, str4); 
745      }
746    
747    public final void debug(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5) {
748      doLog(DEBUG, str1, str2, str3, str4, str5); 
749      }
750    
751    public final void debug(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6) {
752      doLog(DEBUG, str1, str2, str3, str4, str5, str6); 
753      }
754    
755    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) {
756      doLog(DEBUG, str1, str2, str3, str4, str5, str6, str7); 
757      }
758    
759    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) {
760      doLog(DEBUG, str1, str2, str3, str4, str5, str6, str7, args); 
761      }
762    
763    public final void bug(final Object str1) {
764      doLog(DEBUG, str1); 
765      }
766    
767    public final void bug(final Object str1, final Object str2) {
768      doLog(DEBUG, str1, str2); 
769      }
770      
771    public final void bug(final Object str1, final Object str2, final Object str3) {
772      doLog(DEBUG, str1, str2, str3); 
773      }
774    
775    public final void bug(final Object str1, final Object str2, final Object str3, final Object str4) {
776      doLog(DEBUG, str1, str2, str3, str4); 
777      }
778    
779    public final void bug(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5) {
780      doLog(DEBUG, str1, str2, str3, str4, str5); 
781      }
782    
783    public final void bug(final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6) {
784      doLog(DEBUG, str1, str2, str3, str4, str5, str6); 
785      }
786    
787    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) {
788      doLog(DEBUG, str1, str2, str3, str4, str5, str6, str7); 
789      }
790    
791    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) {
792      doLog(DEBUG, str1, str2, str3, str4, str5, str6, str7, args); 
793      }
794      
795    final void doLog(final LogLevel level, final Object str1) 
796      {
797      if (level.intval > currentLevel.intval)
798        return;
799      
800      log(level, getDebugContext(level, 3), str1);
801      }
802    
803    final void doLog(final LogLevel level, final Object str1, final Object str2) 
804      {
805      if (level.intval > currentLevel.intval)
806        return;
807      
808      log(level, getDebugContext(level, 3), str1, str2);
809      }
810    
811    final void doLog(final LogLevel level, final Object str1, 
812             final Object str2, final Object str3) 
813      {
814      if (level.intval > currentLevel.intval)
815        return;
816      
817      log(level, getDebugContext(level, 3), str1, str2, str3);
818      }
819    
820    final void doLog(final LogLevel level, final Object str1, final Object str2, 
821             final Object str3, final Object str4) 
822      {
823      if (level.intval > currentLevel.intval)
824        return;
825      
826      log(level, getDebugContext(level, 3), str1, str2, str3, str4);
827      }
828    
829    final void doLog(final LogLevel level, final Object str1, final Object str2, 
830             final Object str3, final Object str4, final Object str5) 
831      {
832      if (level.intval > currentLevel.intval)
833        return;
834      
835      log(level, getDebugContext(level, 3), str1, str2, str3, str4, str5);
836      }
837    
838    final void doLog(final LogLevel level, final Object str1, final Object str2, 
839             final Object str3, final Object str4, final Object str5, 
840             final Object str6) 
841      {
842      if (level.intval > currentLevel.intval)
843        return;
844      
845      log(level, getDebugContext(level, 3), str1, str2, str3, str4, str5, str6);
846      }
847    
848    final void doLog(final LogLevel level, final Object str1, final Object str2, 
849             final Object str3, final Object str4, final Object str5, 
850             final Object str6, final Object str7) 
851      {
852      if (level.intval > currentLevel.intval)
853        return;
854      
855      log(level, getDebugContext(level, 3), str1, str2, str3, str4, str5, str6, str7);
856      }
857    
858    final void doLog(final LogLevel level, final Object str1, final Object str2, 
859             final Object str3, final Object str4, final Object str5, 
860             final Object str6, final Object str7, Object... args) 
861      {
862      if (level.intval > currentLevel.intval)
863        return;
864      
865      log(level, getDebugContext(level, 3), str1, str2, str3, str4, str5, str6, str7, args);
866      }
867    
868    
869    //--abstract methods--
870    
871    public abstract void close();
872    
873    
874    /**
875    @param  level the current log level. This can be logged
876            as well.
877    @param  str1  unless overridden in a subclass, this is the
878            value returned by {@link getDebugContext} and
879            is generated automatically by the warn(), 
880            info(), debug() etc., methods
881    */
882    public abstract void log(LogLevel level, Object str1); 
883    
884    /**
885    @param  level the current log level. This can be logged
886            as well.
887    @param  str1  unless overridden in a subclass, this is the
888            value returned by {@link getDebugContext} and
889            is generated automatically by the warn(), 
890            info(), debug() etc., methods
891    @param  str2_onwards  
892            some arbitrary object
893    */
894    public abstract void log(LogLevel level, final Object str1, final Object str2);
895    
896    /**
897    @param  level the current log level. This can be logged
898            as well.
899    @param  str1  unless overridden in a subclass, this is the
900            value returned by {@link getDebugContext} and
901            is generated automatically by the warn(), 
902            info(), debug() etc., methods
903    @param  str2_onwards  
904            some arbitrary object
905    */
906    public abstract void log(LogLevel level, final Object str1, final Object str2, final Object str3);
907    
908    /**
909    @param  level the current log level. This can be logged
910            as well.
911    @param  str1  unless overridden in a subclass, this is the
912            value returned by {@link getDebugContext} and
913            is generated automatically by the warn(), 
914            info(), debug() etc., methods
915    @param  str2_onwards  
916            some arbitrary object
917    */
918    public abstract void log(LogLevel level, final Object str1, final Object str2, final Object str3, final Object str4);
919    
920    /**
921    @param  level the current log level. This can be logged
922            as well.
923    @param  str1  unless overridden in a subclass, this is the
924            value returned by {@link getDebugContext} and
925            is generated automatically by the warn(), 
926            info(), debug() etc., methods
927    @param  str2_onwards  
928            some arbitrary object
929    */
930    public abstract void log(LogLevel level, final Object str1, final Object str2, final Object str3, final Object str4, final Object str5);
931    
932    /**
933    @param  level the current log level. This can be logged
934            as well.
935    @param  str1  unless overridden in a subclass, this is the
936            value returned by {@link getDebugContext} and
937            is generated automatically by the warn(), 
938            info(), debug() etc., methods
939    @param  str2_onwards  
940            some arbitrary object
941    */
942    public abstract void log(LogLevel level, final Object str1, final Object str2, final Object str3, final Object str4, final Object str5, final Object str6);
943    
944    /**
945    @param  level the current log level. This can be logged
946            as well.
947    @param  str1  unless overridden in a subclass, this is the
948            value returned by {@link getDebugContext} and
949            is generated automatically by the warn(), 
950            info(), debug() etc., methods
951    @param  str2_onwards  
952            some arbitrary object
953    */
954    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);
955    
956    /**
957    @param  level the current log level. This can be logged
958            as well.
959    @param  str1  unless overridden in a subclass, this is the
960            value returned by {@link getDebugContext} and
961            is generated automatically by the warn(), 
962            info(), debug() etc., methods
963    @param  str2_onwards  
964            some arbitrary object
965    */
966    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);
967    
968    /**
969    @param  level the current log level. This can be logged
970            as well.
971    @param  str1  unless overridden in a subclass, this is the
972            value returned by {@link getDebugContext} and
973            is generated automatically by the warn(), 
974            info(), debug() etc., methods
975    @param  str2_onwards  
976            some arbitrary object
977    */
978    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);
979    }          //~class Log
980    
981