Author: ravn
Date: Sun Dec 28 00:37:28 2008
New Revision: 1263

Modified:
   
slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/instrumentation/LogTransformer.java
   slf4j/trunk/slf4j-site/src/site/pages/extensions.html

Log:
Revised documentation and ensured that log4j and logback classes are not 
instrumented


Modified: 
slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/instrumentation/LogTransformer.java
==============================================================================
--- 
slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/instrumentation/LogTransformer.java
   (original)
+++ 
slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/instrumentation/LogTransformer.java
   Sun Dec 28 00:37:28 2008
@@ -30,268 +30,272 @@
  */
 public class LogTransformer implements ClassFileTransformer {
 
-  /**
-   * Builder provides a flexible way of configuring some of many options on the
-   * parent class instead of providing many constructors.
-   * 
-   * {...@link http
-   * ://rwhansen.blogspot.com/2007/07/theres-builder-pattern-that-joshua.html}
-   * 
-   */
-  public static class Builder {
-
-    /**
-     * Build and return the LogTransformer corresponding to the options set in
-     * this Builder.
-     * 
-     * @return
-     */
-    public LogTransformer build() {
-      if (verbose) {
-        System.err.println("Creating LogTransformer");
-      }
-      return new LogTransformer(this);
-    }
-
-    boolean addEntryExit;
-
-    /**
-     * Should each method log entry (with parameters) and exit (with parameters
-     * and returnvalue)?
-     * 
-     * @param b
-     *          value of flag
-     * @return
-     */
-    public Builder addEntryExit(boolean b) {
-      addEntryExit = b;
-      return this;
-    }
-
-    boolean addVariableAssignment;
-
-    // private Builder addVariableAssignment(boolean b) {
-    // System.err.println("cannot currently log variable assignments.");
-    // addVariableAssignment = b;
-    // return this;
-    // }
-
-    boolean verbose;
-
-    /**
-     * Should LogTransformer be verbose in what it does? This currently list 
the
-     * names of the classes being processed.
-     * 
-     * @param b
-     * @return
-     */
-    public Builder verbose(boolean b) {
-      verbose = b;
-      return this;
-    }
-
-    String[] ignore = {"org/slf4j/"};
-
-    public Builder ignore(String[] strings) {
-      this.ignore = strings;
-      return this;
-    }
-
-    private String level = "info";
-
-    public Builder level(String level) {
-      level = level.toLowerCase();
-      if (level.equals("info") || level.equals("debug")
-          || level.equals("trace")) {
-        this.level = level;
-      } else {
-        if (verbose) {
-          System.err.println("level not info/debug/trace : " + level);
-        }
-      }
-      return this;
-    }
-  }
-
-  private String level;
-  private String levelEnabled;
-
-  private LogTransformer(Builder builder) {
-    String s = "WARNING: javassist not available on classpath for javaagent, 
log statements will not be added";
-    try {
-      if (Class.forName("javassist.ClassPool") == null) {
-        System.err.println(s);
-      }
-    } catch (ClassNotFoundException e) {
-      System.err.println(s);
-    }
-
-    this.addEntryExit = builder.addEntryExit;
-    // this.addVariableAssignment = builder.addVariableAssignment;
-    this.verbose = builder.verbose;
-    this.ignore = builder.ignore;
-    this.level = builder.level;
-    this.levelEnabled = "is" + builder.level.substring(0, 1).toUpperCase()
-        + builder.level.substring(1) + "Enabled";
-  }
-
-  private boolean addEntryExit;
-  // private boolean addVariableAssignment;
-  private boolean verbose;
-  private String[] ignore;
-
-  public byte[] transform(ClassLoader loader, String className, Class<?> clazz,
-      ProtectionDomain domain, byte[] bytes) {
-
-    try {
-      return transform0(className, clazz, domain, bytes);
-    } catch (Exception e) {
-      System.err.println("Could not instrument " + className);
-      e.printStackTrace();
-      return bytes;
-    }
-  }
-
-  /**
-   * transform0 sees if the className starts with any of the namespaces to
-   * ignore, if so it is returned unchanged. Otherwise it is processed by
-   * doClass(...)
-   * 
-   * @param className
-   * @param clazz
-   * @param domain
-   * @param bytes
-   * @return
-   */
-
-  private byte[] transform0(String className, Class<?> clazz,
-      ProtectionDomain domain, byte[] bytes) {
-
-    try {
-      for (int i = 0; i < ignore.length; i++) {
-        if (className.startsWith(ignore[i])) {
-          return bytes;
-        }
-      }
-      String slf4jName = "org.slf4j.LoggerFactory";
-      try {
-        if (domain != null && domain.getClassLoader() != null) {
-          domain.getClassLoader().loadClass(slf4jName);
-        } else {
-          if (verbose) {
-            System.err.println("Skipping " + className
-                + " as it doesn't have a domain or a class loader.");
-          }
-          return bytes;
-        }
-      } catch (ClassNotFoundException e) {
-        if (verbose) {
-          System.err.println("Skipping " + className
-              + " as slf4j is not available to it");
-        }
-        return bytes;
-      }
-      if (verbose) {
-        System.err.println("Processing " + className);
-      }
-      return doClass(className, clazz, bytes);
-    } catch (Throwable e) {
-      System.out.println("e = " + e);
-      return bytes;
-    }
-  }
-
-  private String loggerName;
-
-  /**
-   * doClass() process a single class by first creates a class description from
-   * the byte codes. If it is a class (i.e. not an interface) the methods
-   * defined have bodies, and a static final logger object is added with the
-   * name of this class as an argument, and each method then gets processed 
with
-   * doMethod(...) to have logger calls added.
-   * 
-   * @param name
-   *          class name (slashes separate, not dots)
-   * @param clazz
-   * @param b
-   * @return
-   */
-  private byte[] doClass(String name, Class<?> clazz, byte[] b) {
-    ClassPool pool = ClassPool.getDefault();
-    CtClass cl = null;
-    try {
-      cl = pool.makeClass(new ByteArrayInputStream(b));
-      if (cl.isInterface() == false) {
-
-        loggerName = "_____log";
-
-        // We have to declare the log variable.
-
-        String pattern1 = "private static org.slf4j.Logger {};";
-        String loggerDefinition = format(pattern1, loggerName);
-        CtField field = CtField.make(loggerDefinition, cl);
-
-        // and assign it the appropriate value.
-
-        String pattern2 = "org.slf4j.LoggerFactory.getLogger({}.class);";
-        String replace = name.replace('/', '.');
-        String getLogger = format(pattern2, replace);
-
-        cl.addField(field, getLogger);
-
-        // then check every behaviour (which includes methods). We are only
-        // interested in non-empty ones, as they have code.
-        // NOTE: This will be changed, as empty methods should be
-        // instrumented too.
-
-        CtBehavior[] methods = cl.getDeclaredBehaviors();
-        for (int i = 0; i < methods.length; i++) {
-          if (methods[i].isEmpty() == false) {
-            doMethod(methods[i]);
-          }
-        }
-        b = cl.toBytecode();
-      }
-    } catch (Exception e) {
-      System.err.println("Could not instrument " + name + ", " + e);
-      e.printStackTrace(System.err);
-    } finally {
-      if (cl != null) {
-        cl.detach();
-      }
-    }
-    return b;
-  }
-
-  /**
-   * process a single method - this means add entry/exit logging if requested.
-   * It is only called for methods with a body.
-   * 
-   * @param method
-   *          method to work on
-   * @throws NotFoundException
-   * @throws CannotCompileException
-   */
-  private void doMethod(CtBehavior method) throws NotFoundException,
-      CannotCompileException {
-
-    String signature = JavassistHelper.getSignature(method);
-    String returnValue = JavassistHelper.returnValue(method);
-
-    if (addEntryExit) {
-      String messagePattern = "if ({}.{}()) {}.{}(\">> {}\");";
-      Object[] arg1 = new Object[] { loggerName, levelEnabled, loggerName,
-          level, signature };
-      String before = MessageFormatter.arrayFormat(messagePattern, arg1);
-      // System.out.println(before);
-      method.insertBefore(before);
-
-      String messagePattern2 = "if ({}.{}()) {}.{}(\"<< {}{}\");";
-      Object[] arg2 = new Object[] { loggerName, levelEnabled, loggerName,
-          level, signature, returnValue };
-      String after = MessageFormatter.arrayFormat(messagePattern2, arg2);
-      // System.out.println(after);
-      method.insertAfter(after);
-    }
-  }
+       /**
+        * Builder provides a flexible way of configuring some of many options 
on
+        * the parent class instead of providing many constructors.
+        * 
+        * {...@link http 
+        * 
://rwhansen.blogspot.com/2007/07/theres-builder-pattern-that-joshua.html}
+        * 
+        */
+       public static class Builder {
+
+               /**
+                * Build and return the LogTransformer corresponding to the 
options set
+                * in this Builder.
+                * 
+                * @return
+                */
+               public LogTransformer build() {
+                       if (verbose) {
+                               System.err.println("Creating LogTransformer");
+                       }
+                       return new LogTransformer(this);
+               }
+
+               boolean addEntryExit;
+
+               /**
+                * Should each method log entry (with parameters) and exit (with
+                * parameters and returnvalue)?
+                * 
+                * @param b
+                *            value of flag
+                * @return
+                */
+               public Builder addEntryExit(boolean b) {
+                       addEntryExit = b;
+                       return this;
+               }
+
+               boolean addVariableAssignment;
+
+               // private Builder addVariableAssignment(boolean b) {
+               // System.err.println("cannot currently log variable 
assignments.");
+               // addVariableAssignment = b;
+               // return this;
+               // }
+
+               boolean verbose;
+
+               /**
+                * Should LogTransformer be verbose in what it does? This 
currently list
+                * the names of the classes being processed.
+                * 
+                * @param b
+                * @return
+                */
+               public Builder verbose(boolean b) {
+                       verbose = b;
+                       return this;
+               }
+
+               String[] ignore = { "org/slf4j/", "ch/qos/logback/",
+                               "org/apache/log4j/" };
+
+               public Builder ignore(String[] strings) {
+                       this.ignore = strings;
+                       return this;
+               }
+
+               private String level = "info";
+
+               public Builder level(String level) {
+                       level = level.toLowerCase();
+                       if (level.equals("info") || level.equals("debug")
+                                       || level.equals("trace")) {
+                               this.level = level;
+                       } else {
+                               if (verbose) {
+                                       System.err.println("level not 
info/debug/trace : " + level);
+                               }
+                       }
+                       return this;
+               }
+       }
+
+       private String level;
+       private String levelEnabled;
+
+       private LogTransformer(Builder builder) {
+               String s = "WARNING: javassist not available on classpath for 
javaagent, log statements will not be added";
+               try {
+                       if (Class.forName("javassist.ClassPool") == null) {
+                               System.err.println(s);
+                       }
+               } catch (ClassNotFoundException e) {
+                       System.err.println(s);
+               }
+
+               this.addEntryExit = builder.addEntryExit;
+               // this.addVariableAssignment = builder.addVariableAssignment;
+               this.verbose = builder.verbose;
+               this.ignore = builder.ignore;
+               this.level = builder.level;
+               this.levelEnabled = "is" + builder.level.substring(0, 
1).toUpperCase()
+                               + builder.level.substring(1) + "Enabled";
+       }
+
+       private boolean addEntryExit;
+       // private boolean addVariableAssignment;
+       private boolean verbose;
+       private String[] ignore;
+
+       public byte[] transform(ClassLoader loader, String className,
+                       Class<?> clazz, ProtectionDomain domain, byte[] bytes) {
+
+               try {
+                       return transform0(className, clazz, domain, bytes);
+               } catch (Exception e) {
+                       System.err.println("Could not instrument " + className);
+                       e.printStackTrace();
+                       return bytes;
+               }
+       }
+
+       /**
+        * transform0 sees if the className starts with any of the namespaces to
+        * ignore, if so it is returned unchanged. Otherwise it is processed by
+        * doClass(...)
+        * 
+        * @param className
+        * @param clazz
+        * @param domain
+        * @param bytes
+        * @return
+        */
+
+       private byte[] transform0(String className, Class<?> clazz,
+                       ProtectionDomain domain, byte[] bytes) {
+
+               try {
+                       for (int i = 0; i < ignore.length; i++) {
+                               if (className.startsWith(ignore[i])) {
+                                       return bytes;
+                               }
+                       }
+                       String slf4jName = "org.slf4j.LoggerFactory";
+                       try {
+                               if (domain != null && domain.getClassLoader() 
!= null) {
+                                       
domain.getClassLoader().loadClass(slf4jName);
+                               } else {
+                                       if (verbose) {
+                                               System.err
+                                                               
.println("Skipping "
+                                                                               
+ className
+                                                                               
+ " as it doesn't have a domain or a class loader.");
+                                       }
+                                       return bytes;
+                               }
+                       } catch (ClassNotFoundException e) {
+                               if (verbose) {
+                                       System.err.println("Skipping " + 
className
+                                                       + " as slf4j is not 
available to it");
+                               }
+                               return bytes;
+                       }
+                       if (verbose) {
+                               System.err.println("Processing " + className);
+                       }
+                       return doClass(className, clazz, bytes);
+               } catch (Throwable e) {
+                       System.out.println("e = " + e);
+                       return bytes;
+               }
+       }
+
+       private String loggerName;
+
+       /**
+        * doClass() process a single class by first creates a class description
+        * from the byte codes. If it is a class (i.e. not an interface) the 
methods
+        * defined have bodies, and a static final logger object is added with 
the
+        * name of this class as an argument, and each method then gets 
processed
+        * with doMethod(...) to have logger calls added.
+        * 
+        * @param name
+        *            class name (slashes separate, not dots)
+        * @param clazz
+        * @param b
+        * @return
+        */
+       private byte[] doClass(String name, Class<?> clazz, byte[] b) {
+               ClassPool pool = ClassPool.getDefault();
+               CtClass cl = null;
+               try {
+                       cl = pool.makeClass(new ByteArrayInputStream(b));
+                       if (cl.isInterface() == false) {
+
+                               loggerName = "_____log";
+
+                               // We have to declare the log variable.
+
+                               String pattern1 = "private static 
org.slf4j.Logger {};";
+                               String loggerDefinition = format(pattern1, 
loggerName);
+                               CtField field = CtField.make(loggerDefinition, 
cl);
+
+                               // and assign it the appropriate value.
+
+                               String pattern2 = 
"org.slf4j.LoggerFactory.getLogger({}.class);";
+                               String replace = name.replace('/', '.');
+                               String getLogger = format(pattern2, replace);
+
+                               cl.addField(field, getLogger);
+
+                               // then check every behaviour (which includes 
methods). We are
+                               // only
+                               // interested in non-empty ones, as they have 
code.
+                               // NOTE: This will be changed, as empty methods 
should be
+                               // instrumented too.
+
+                               CtBehavior[] methods = 
cl.getDeclaredBehaviors();
+                               for (int i = 0; i < methods.length; i++) {
+                                       if (methods[i].isEmpty() == false) {
+                                               doMethod(methods[i]);
+                                       }
+                               }
+                               b = cl.toBytecode();
+                       }
+               } catch (Exception e) {
+                       System.err.println("Could not instrument " + name + ", 
" + e);
+                       e.printStackTrace(System.err);
+               } finally {
+                       if (cl != null) {
+                               cl.detach();
+                       }
+               }
+               return b;
+       }
+
+       /**
+        * process a single method - this means add entry/exit logging if 
requested.
+        * It is only called for methods with a body.
+        * 
+        * @param method
+        *            method to work on
+        * @throws NotFoundException
+        * @throws CannotCompileException
+        */
+       private void doMethod(CtBehavior method) throws NotFoundException,
+                       CannotCompileException {
+
+               String signature = JavassistHelper.getSignature(method);
+               String returnValue = JavassistHelper.returnValue(method);
+
+               if (addEntryExit) {
+                       String messagePattern = "if ({}.{}()) {}.{}(\">> 
{}\");";
+                       Object[] arg1 = new Object[] { loggerName, levelEnabled,
+                                       loggerName, level, signature };
+                       String before = 
MessageFormatter.arrayFormat(messagePattern, arg1);
+                       // System.out.println(before);
+                       method.insertBefore(before);
+
+                       String messagePattern2 = "if ({}.{}()) {}.{}(\"<< 
{}{}\");";
+                       Object[] arg2 = new Object[] { loggerName, levelEnabled,
+                                       loggerName, level, signature, 
returnValue };
+                       String after = 
MessageFormatter.arrayFormat(messagePattern2, arg2);
+                       // System.out.println(after);
+                       method.insertAfter(after);
+               }
+       }
 }
\ No newline at end of file

Modified: slf4j/trunk/slf4j-site/src/site/pages/extensions.html
==============================================================================
--- slf4j/trunk/slf4j-site/src/site/pages/extensions.html       (original)
+++ slf4j/trunk/slf4j-site/src/site/pages/extensions.html       Sun Dec 28 
00:37:28 2008
@@ -386,7 +386,7 @@
    logging in a standardized manner.
    </p>
 
-   <p>Note thar XLogger instances are obrained to through the 
+   <p>Note that XLogger instances are obtained to through the 
    <a
    
href="apidocs/org/slf4j/ext/XLoggerFactory.html"><code>XLoggerFactory</code></a>
    utility class.</p>
@@ -676,7 +676,8 @@
     <dt><b>ignore</b>=X:Y:...</dt>
     <dd>(Advanced) Provide full list of colon separated prefixes of
     class names NOT to add logging to.  The default list is 
-    
"sun/:java/:javax/:org/slf4j/:ch/qos/logback/:org/apache/log4j/:apple/:com/sun/".
+    "org/slf4j/:ch/qos/logback/:org/apache/log4j/".  This does not override 
the fact that a class must be able to access the 
+    slf4j-api classes in order to do logging, so if these classes are  not 
visible to a given class it is not instrumented. 
     </dd>
   </dl>
   
@@ -701,19 +702,18 @@
   </ul>
 
   <p>A warning message is printed if the javassist library was not
-  found by the agent.
+  found by the agent, and options requiring byte code transformations will not 
work.
   </p>
 
 
   <h3>Misc notes</h3>
 
   <ul>
-    <li>A java agent does not "see" any classes already loaded by the
+    <li>A java agent is not invoked on any classes already loaded by the
     class loader.</li>
-    <li>Any exceptions in the java agent that would normally have been
-    printed, are silently swallowed by the JVM.</li>
-    <li>The javaagent does not do any logging itself, and the slf4j
-    backend does not need to be available to the agent. </li>
+    <li>Exceptions in the java agent that would normally have been
+    printed, may be silently swallowed by the JVM.</li>
+    <li>The javaagent only logs to System.err.</li>
     <li>The name of the logger variable is fixed (to a value unlikely to be 
used) so if that
     name is already used, a failure occures.  This should be changed to 
determine
     an unused name and use that instead.</li>
_______________________________________________
dev mailing list
dev@slf4j.org
http://www.slf4j.org/mailman/listinfo/dev

Reply via email to