X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=srcjar%2Forg%2Fapache%2Flog4j%2Fspi%2FLocationInfo.java;fp=srcjar%2Forg%2Fapache%2Flog4j%2Fspi%2FLocationInfo.java;h=2da5486008d6b7591c952eea783a6fcb6f3af5a5;hb=2d6292c0377bc6b773c6844a45d3f2c5fac352c7;hp=0000000000000000000000000000000000000000;hpb=954af328a2a6a0055572cd1a09ee035301222574;p=jalview.git diff --git a/srcjar/org/apache/log4j/spi/LocationInfo.java b/srcjar/org/apache/log4j/spi/LocationInfo.java new file mode 100644 index 0000000..2da5486 --- /dev/null +++ b/srcjar/org/apache/log4j/spi/LocationInfo.java @@ -0,0 +1,407 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Contributors: Mathias Rupprecht + +package org.apache.log4j.spi; + +import org.apache.log4j.Layout; +import org.apache.log4j.helpers.LogLog; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.InterruptedIOException; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +/** + The internal representation of caller location information. + + @since 0.8.3 +*/ +public class LocationInfo implements java.io.Serializable { + + /** + Caller's line number. + */ + transient String lineNumber; + /** + Caller's file name. + */ + transient String fileName; + /** + Caller's fully qualified class name. + */ + transient String className; + /** + Caller's method name. + */ + transient String methodName; + /** + All available caller information, in the format + fully.qualified.classname.of.caller.methodName(Filename.java:line) + */ + public String fullInfo; + + private static StringWriter sw = new StringWriter(); + private static PrintWriter pw = new PrintWriter(sw); + + private static Method getStackTraceMethod; + private static Method getClassNameMethod; + private static Method getMethodNameMethod; + private static Method getFileNameMethod; + private static Method getLineNumberMethod; + + + /** + When location information is not available the constant + NA is returned. Current value of this string + constant is ?. */ + public final static String NA = "?"; + + static final long serialVersionUID = -1325822038990805636L; + + /** + * NA_LOCATION_INFO is provided for compatibility with log4j 1.3. + * @since 1.2.15 + */ + public static final LocationInfo NA_LOCATION_INFO = + new LocationInfo(NA, NA, NA, NA); + + + + // Check if we are running in IBM's visual age. + static boolean inVisualAge = false; + static { + try { + inVisualAge = Class.forName("com.ibm.uvm.tools.DebugSupport") != null; + LogLog.debug("Detected IBM VisualAge environment."); + } catch(Throwable e) { + // nothing to do + } + try { + Class[] noArgs = null; + getStackTraceMethod = Throwable.class.getMethod("getStackTrace", noArgs); + Class stackTraceElementClass = Class.forName("java.lang.StackTraceElement"); + getClassNameMethod = stackTraceElementClass.getMethod("getClassName", noArgs); + getMethodNameMethod = stackTraceElementClass.getMethod("getMethodName", noArgs); + getFileNameMethod = stackTraceElementClass.getMethod("getFileName", noArgs); + getLineNumberMethod = stackTraceElementClass.getMethod("getLineNumber", noArgs); + } catch(ClassNotFoundException ex) { + LogLog.debug("LocationInfo will use pre-JDK 1.4 methods to determine location."); + } catch(NoSuchMethodException ex) { + LogLog.debug("LocationInfo will use pre-JDK 1.4 methods to determine location."); + } + } + + /** + Instantiate location information based on a Throwable. We + expect the Throwable t, to be in the format + +
+        java.lang.Throwable
+        ...
+          at org.apache.log4j.PatternLayout.format(PatternLayout.java:413)
+          at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183)
+        at org.apache.log4j.Category.callAppenders(Category.java:131)
+        at org.apache.log4j.Category.log(Category.java:512)
+        at callers.fully.qualified.className.methodName(FileName.java:74)
+	...
+       
+ +

However, we can also deal with JIT compilers that "lose" the + location information, especially between the parentheses. + @param t throwable used to determine location, may be null. + @param fqnOfCallingClass class name of first class considered part of + the logging framework. Location will be site that calls a method on this class. + + */ + public LocationInfo(Throwable t, String fqnOfCallingClass) { + if(t == null || fqnOfCallingClass == null) { + return; + } + if (getLineNumberMethod != null) { + try { + Object[] noArgs = null; + Object[] elements = (Object[]) getStackTraceMethod.invoke(t, noArgs); + String prevClass = NA; + for(int i = elements.length - 1; i >= 0; i--) { + String thisClass = (String) getClassNameMethod.invoke(elements[i], noArgs); + if(fqnOfCallingClass.equals(thisClass)) { + int caller = i + 1; + if (caller < elements.length) { + className = prevClass; + methodName = (String) getMethodNameMethod.invoke(elements[caller], noArgs); + fileName = (String) getFileNameMethod.invoke(elements[caller], noArgs); + if (fileName == null) { + fileName = NA; + } + int line = ((Integer) getLineNumberMethod.invoke(elements[caller], noArgs)).intValue(); + if (line < 0) { + lineNumber = NA; + } else { + lineNumber = String.valueOf(line); + } + StringBuffer buf = new StringBuffer(); + buf.append(className); + buf.append("."); + buf.append(methodName); + buf.append("("); + buf.append(fileName); + buf.append(":"); + buf.append(lineNumber); + buf.append(")"); + this.fullInfo = buf.toString(); + } + return; + } + prevClass = thisClass; + } + return; + } catch(IllegalAccessException ex) { + LogLog.debug("LocationInfo failed using JDK 1.4 methods", ex); + } catch(InvocationTargetException ex) { + if (ex.getTargetException() instanceof InterruptedException + || ex.getTargetException() instanceof InterruptedIOException) { + Thread.currentThread().interrupt(); + } + LogLog.debug("LocationInfo failed using JDK 1.4 methods", ex); + } catch(RuntimeException ex) { + LogLog.debug("LocationInfo failed using JDK 1.4 methods", ex); + } + } + + String s; + // Protect against multiple access to sw. + synchronized(sw) { + t.printStackTrace(pw); + s = sw.toString(); + sw.getBuffer().setLength(0); + } + //System.out.println("s is ["+s+"]."); + int ibegin, iend; + + // Given the current structure of the package, the line + // containing "org.apache.log4j.Category." should be printed just + // before the caller. + + // This method of searching may not be fastest but it's safer + // than counting the stack depth which is not guaranteed to be + // constant across JVM implementations. + ibegin = s.lastIndexOf(fqnOfCallingClass); + if(ibegin == -1) { + return; + } + + // + // if the next character after the class name exists + // but is not a period, see if the classname is + // followed by a period earlier in the trace. + // Minimizes mistakeningly matching on a class whose + // name is a substring of the desired class. + // See bug 44888. + if (ibegin + fqnOfCallingClass.length() < s.length() && + s.charAt(ibegin + fqnOfCallingClass.length()) != '.') { + int i = s.lastIndexOf(fqnOfCallingClass + "."); + if (i != -1) { + ibegin = i; + } + } + + + ibegin = s.indexOf(Layout.LINE_SEP, ibegin); + if(ibegin == -1) { + return; + } + ibegin+= Layout.LINE_SEP_LEN; + + // determine end of line + iend = s.indexOf(Layout.LINE_SEP, ibegin); + if(iend == -1) { + return; + } + + // VA has a different stack trace format which doesn't + // need to skip the inital 'at' + if(!inVisualAge) { + // back up to first blank character + ibegin = s.lastIndexOf("at ", iend); + if(ibegin == -1) { + return; + } + // Add 3 to skip "at "; + ibegin += 3; + } + // everything between is the requested stack item + this.fullInfo = s.substring(ibegin, iend); + } + + /** + * Appends a location fragment to a buffer to build the + * full location info. + * @param buf StringBuffer to receive content. + * @param fragment fragment of location (class, method, file, line), + * if null the value of NA will be appended. + * @since 1.2.15 + */ + private static final void appendFragment(final StringBuffer buf, + final String fragment) { + if (fragment == null) { + buf.append(NA); + } else { + buf.append(fragment); + } + } + + /** + * Create new instance. + * @param file source file name + * @param classname class name + * @param method method + * @param line source line number + * + * @since 1.2.15 + */ + public LocationInfo( + final String file, + final String classname, + final String method, + final String line) { + this.fileName = file; + this.className = classname; + this.methodName = method; + this.lineNumber = line; + StringBuffer buf = new StringBuffer(); + appendFragment(buf, classname); + buf.append("."); + appendFragment(buf, method); + buf.append("("); + appendFragment(buf, file); + buf.append(":"); + appendFragment(buf, line); + buf.append(")"); + this.fullInfo = buf.toString(); + } + + /** + Return the fully qualified class name of the caller making the + logging request. + */ + public + String getClassName() { + if(fullInfo == null) { + return NA; + } + if(className == null) { + // Starting the search from '(' is safer because there is + // potentially a dot between the parentheses. + int iend = fullInfo.lastIndexOf('('); + if(iend == -1) { + className = NA; + } else { + iend =fullInfo.lastIndexOf('.', iend); + + // This is because a stack trace in VisualAge looks like: + + //java.lang.RuntimeException + // java.lang.Throwable() + // java.lang.Exception() + // java.lang.RuntimeException() + // void test.test.B.print() + // void test.test.A.printIndirect() + // void test.test.Run.main(java.lang.String []) + int ibegin = 0; + if (inVisualAge) { + ibegin = fullInfo.lastIndexOf(' ', iend)+1; + } + + if(iend == -1) { + className = NA; + } else { + className = this.fullInfo.substring(ibegin, iend); + } + } + } + return className; + } + + /** + Return the file name of the caller. + +

This information is not always available. + */ + public + String getFileName() { + if(fullInfo == null) { + return NA; + } + + if(fileName == null) { + int iend = fullInfo.lastIndexOf(':'); + if(iend == -1) { + fileName = NA; + } else { + int ibegin = fullInfo.lastIndexOf('(', iend - 1); + fileName = this.fullInfo.substring(ibegin + 1, iend); + } + } + return fileName; + } + + /** + Returns the line number of the caller. + +

This information is not always available. + */ + public + String getLineNumber() { + if(fullInfo == null) { + return NA; + } + + if(lineNumber == null) { + int iend = fullInfo.lastIndexOf(')'); + int ibegin = fullInfo.lastIndexOf(':', iend -1); + if(ibegin == -1) { + lineNumber = NA; + } else { + lineNumber = this.fullInfo.substring(ibegin + 1, iend); + } + } + return lineNumber; + } + + /** + Returns the method name of the caller. + */ + public + String getMethodName() { + if(fullInfo == null) { + return NA; + } + if(methodName == null) { + int iend = fullInfo.lastIndexOf('('); + int ibegin = fullInfo.lastIndexOf('.', iend); + if(ibegin == -1) { + methodName = NA; + } else { + methodName = this.fullInfo.substring(ibegin + 1, iend); + } + } + return methodName; + } +}