[slf4j-dev] svn commit: r1263 - in slf4j/trunk: slf4j-ext/src/main/java/org/slf4j/instrumentation slf4j-site/src/site/pages

ravn at slf4j.org ravn at slf4j.org
Sun Dec 28 00:37:29 CET 2008


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>



More information about the slf4j-dev mailing list