You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2013/08/07 23:11:16 UTC

svn commit: r1511497 - /tomcat/trunk/java/org/apache/juli/OneLineFormatter.java

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-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org