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