Author: markt Date: Wed Aug 7 21:11:15 2013 New Revision: 1511497 URL: http://svn.apache.org/r1511497 Log: Improve the one line formatter so it outputs the correct thread ID even when using the async log writer.
Modified: tomcat/trunk/java/org/apache/juli/OneLineFormatter.java Modified: tomcat/trunk/java/org/apache/juli/OneLineFormatter.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/juli/OneLineFormatter.java?rev=1511497&r1=1511496&r2=1511497&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/juli/OneLineFormatter.java (original) +++ tomcat/trunk/java/org/apache/juli/OneLineFormatter.java Wed Aug 7 21:11:15 2013 @@ -19,6 +19,12 @@ package org.apache.juli; import java.io.PrintWriter; import java.io.StringWriter; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; import java.util.logging.Formatter; import java.util.logging.LogRecord; @@ -34,6 +40,27 @@ public class OneLineFormatter extends Fo private static final String LINE_SEP = System.getProperty("line.separator"); private static final String ST_SEP = LINE_SEP + " "; + private static final String UNKONWN_THREAD_NAME = "Unknown thread with ID "; + private static final Object threadMxBeanLock = new Object(); + private static volatile ThreadMXBean threadMxBean = null; + private static final int THREAD_NAME_CACHE_SIZE = 10000; + private static ThreadLocal<LinkedHashMap<Integer,String>> threadNameCache = + new ThreadLocal<LinkedHashMap<Integer,String>>() { + + @Override + protected LinkedHashMap<Integer,String> initialValue() { + return new LinkedHashMap<Integer,String>() { + + private static final long serialVersionUID = 1L; + + @Override + protected boolean removeEldestEntry( + Entry<Integer, String> eldest) { + return (size() > THREAD_NAME_CACHE_SIZE); + } + }; + } + }; /* Timestamp format */ private static final String timeFormat = "dd-MMM-yyyy HH:mm:ss"; @@ -79,7 +106,13 @@ public class OneLineFormatter extends Fo // Thread sb.append(' '); sb.append('['); - sb.append(Thread.currentThread().getName()); + if (Thread.currentThread() instanceof AsyncFileHandler.LoggerThread) { + // If using the async handler can't get the thread name from the + // current thread. + sb.append(getThreadName(record.getThreadID())); + } else { + sb.append(Thread.currentThread().getName()); + } sb.append(']'); // Source @@ -122,4 +155,47 @@ public class OneLineFormatter extends Fo } buf.append(frac); } + + + /** + * LogRecord has threadID but no thread name. + * LogRecord uses an int for thread ID but thread IDs are longs. + * If the real thread ID > (Integer.MAXVALUE / 2) LogRecord uses it's own + * ID in an effort to avoid clashes due to overflow. + * <p> + * Words fail me to describe what I think of the design decision to use an + * int in LogRecord for a long value and the resulting mess that follows. + */ + private static String getThreadName(int logRecordThreadId) { + Map<Integer,String> cache = threadNameCache.get(); + String result = null; + + if (logRecordThreadId > (Integer.MAX_VALUE / 2)) { + result = cache.get(Integer.valueOf(logRecordThreadId)); + } + + if (result != null) { + return result; + } + + if (logRecordThreadId > Integer.MAX_VALUE / 2) { + result = UNKONWN_THREAD_NAME + logRecordThreadId; + } else { + // Double checked locking OK as threadMxBean is volatile + if (threadMxBean == null) { + synchronized (threadMxBeanLock) { + if (threadMxBean == null) { + threadMxBean = ManagementFactory.getThreadMXBean(); + } + } + } + ThreadInfo threadInfo = + threadMxBean.getThreadInfo(logRecordThreadId); + result = threadInfo.getThreadName(); + } + + cache.put(Integer.valueOf(logRecordThreadId), result); + + return result; + } } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org