001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.message; 018 019import java.io.InvalidObjectException; 020import java.io.ObjectInputStream; 021import java.io.Serializable; 022import java.lang.management.ManagementFactory; 023import java.lang.management.ThreadInfo; 024import java.lang.management.ThreadMXBean; 025import java.lang.reflect.Method; 026import java.util.HashMap; 027import java.util.Map; 028 029import org.apache.logging.log4j.util.StringBuilders; 030import org.apache.logging.log4j.util.Strings; 031 032/** 033 * Captures information about all running Threads. 034 */ 035public class ThreadDumpMessage implements Message { 036 037 private static final long serialVersionUID = -1103400781608841088L; 038 039 private static final ThreadInfoFactory FACTORY; 040 041 private volatile Map<ThreadInformation, StackTraceElement[]> threads; 042 043 private final String title; 044 045 private String formattedMessage; 046 047 static { 048 final Method[] methods = ThreadInfo.class.getMethods(); 049 boolean basic = true; 050 for (final Method method : methods) { 051 if (method.getName().equals("getLockInfo")) { 052 basic = false; 053 break; 054 } 055 } 056 FACTORY = basic ? new BasicThreadInfoFactory() : new ExtendedThreadInfoFactory(); 057 } 058 059 /** 060 * Generate a ThreadDumpMessage with a title. 061 * @param title The title. 062 */ 063 public ThreadDumpMessage(final String title) { 064 this.title = title == null ? Strings.EMPTY : title; 065 threads = FACTORY.createThreadInfo(); 066 } 067 068 private ThreadDumpMessage(final String formattedMsg, final String title) { 069 this.formattedMessage = formattedMsg; 070 this.title = title == null ? Strings.EMPTY : title; 071 } 072 073 @Override 074 public String toString() { 075 final StringBuilder sb = new StringBuilder("ThreadDumpMessage["); 076 if (this.title.length() > 0) { 077 StringBuilders.appendKeyDqValue(sb, "Title", this.title); 078 } 079 sb.append(']'); 080 return sb.toString(); 081 } 082 083 /** 084 * Returns the ThreadDump in printable format. 085 * @return the ThreadDump suitable for logging. 086 */ 087 @Override 088 public String getFormattedMessage() { 089 if (formattedMessage != null) { 090 return formattedMessage; 091 } 092 final StringBuilder sb = new StringBuilder(title); 093 if (title.length() > 0) { 094 sb.append('\n'); 095 } 096 for (final Map.Entry<ThreadInformation, StackTraceElement[]> entry : threads.entrySet()) { 097 final ThreadInformation info = entry.getKey(); 098 info.printThreadInfo(sb); 099 info.printStack(sb, entry.getValue()); 100 sb.append('\n'); 101 } 102 return sb.toString(); 103 } 104 105 /** 106 * Returns the title. 107 * @return the title. 108 */ 109 @Override 110 public String getFormat() { 111 return title == null ? Strings.EMPTY : title; 112 } 113 114 /** 115 * Returns an array with a single element, a Map containing the ThreadInformation as the key. 116 * and the StackTraceElement array as the value; 117 * @return the "parameters" to this Message. 118 */ 119 @Override 120 public Object[] getParameters() { 121 return null; 122 } 123 124 /** 125 * Creates a ThreadDumpMessageProxy that can be serialized. 126 * @return a ThreadDumpMessageProxy. 127 */ 128 protected Object writeReplace() { 129 return new ThreadDumpMessageProxy(this); 130 } 131 132 private void readObject(final ObjectInputStream stream) 133 throws InvalidObjectException { 134 throw new InvalidObjectException("Proxy required"); 135 } 136 137 /** 138 * Proxy pattern used to serialize the ThreadDumpMessage. 139 */ 140 private static class ThreadDumpMessageProxy implements Serializable { 141 142 private static final long serialVersionUID = -3476620450287648269L; 143 private final String formattedMsg; 144 private final String title; 145 146 public ThreadDumpMessageProxy(final ThreadDumpMessage msg) { 147 this.formattedMsg = msg.getFormattedMessage(); 148 this.title = msg.title; 149 } 150 151 /** 152 * Returns a ThreadDumpMessage using the data in the proxy. 153 * @return a ThreadDumpMessage. 154 */ 155 protected Object readResolve() { 156 return new ThreadDumpMessage(formattedMsg, title); 157 } 158 } 159 160 /** 161 * Factory to create Thread information. 162 */ 163 private interface ThreadInfoFactory { 164 Map<ThreadInformation, StackTraceElement[]> createThreadInfo(); 165 } 166 167 /** 168 * Factory to create basic thread information. 169 */ 170 private static class BasicThreadInfoFactory implements ThreadInfoFactory { 171 @Override 172 public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() { 173 final Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces(); 174 final Map<ThreadInformation, StackTraceElement[]> threads = 175 new HashMap<ThreadInformation, StackTraceElement[]>(map.size()); 176 for (final Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) { 177 threads.put(new BasicThreadInformation(entry.getKey()), entry.getValue()); 178 } 179 return threads; 180 } 181 } 182 183 /** 184 * Factory to create extended thread information. 185 */ 186 private static class ExtendedThreadInfoFactory implements ThreadInfoFactory { 187 @Override 188 public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() { 189 final ThreadMXBean bean = ManagementFactory.getThreadMXBean(); 190 final ThreadInfo[] array = bean.dumpAllThreads(true, true); 191 192 final Map<ThreadInformation, StackTraceElement[]> threads = 193 new HashMap<ThreadInformation, StackTraceElement[]>(array.length); 194 for (final ThreadInfo info : array) { 195 threads.put(new ExtendedThreadInformation(info), info.getStackTrace()); 196 } 197 return threads; 198 } 199 } 200 201 /** 202 * Always returns null. 203 * 204 * @return null 205 */ 206 @Override 207 public Throwable getThrowable() { 208 return null; 209 } 210}