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.StringBuilderFormattable; 030import org.apache.logging.log4j.util.Strings; 031 032/** 033 * Captures information about all running Threads. 034 */ 035public class ThreadDumpMessage implements Message, StringBuilderFormattable { 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 return getFormattedMessage(); 076 } 077 078 /** 079 * Returns the ThreadDump in printable format. 080 * @return the ThreadDump suitable for logging. 081 */ 082 @Override 083 public String getFormattedMessage() { 084 if (formattedMessage != null) { 085 return formattedMessage; 086 } 087 final StringBuilder sb = new StringBuilder(255); 088 formatTo(sb); 089 return sb.toString(); 090 } 091 092 @Override 093 public void formatTo(final StringBuilder sb) { 094 sb.append(title); 095 if (title.length() > 0) { 096 sb.append('\n'); 097 } 098 for (final Map.Entry<ThreadInformation, StackTraceElement[]> entry : threads.entrySet()) { 099 final ThreadInformation info = entry.getKey(); 100 info.printThreadInfo(sb); 101 info.printStack(sb, entry.getValue()); 102 sb.append('\n'); 103 } 104 } 105 106 /** 107 * Returns the title. 108 * @return the title. 109 */ 110 @Override 111 public String getFormat() { 112 return title == null ? Strings.EMPTY : title; 113 } 114 115 /** 116 * Returns an array with a single element, a Map containing the ThreadInformation as the key. 117 * and the StackTraceElement array as the value; 118 * @return the "parameters" to this Message. 119 */ 120 @Override 121 public Object[] getParameters() { 122 return null; 123 } 124 125 /** 126 * Creates a ThreadDumpMessageProxy that can be serialized. 127 * @return a ThreadDumpMessageProxy. 128 */ 129 protected Object writeReplace() { 130 return new ThreadDumpMessageProxy(this); 131 } 132 133 private void readObject(final ObjectInputStream stream) 134 throws InvalidObjectException { 135 throw new InvalidObjectException("Proxy required"); 136 } 137 138 /** 139 * Proxy pattern used to serialize the ThreadDumpMessage. 140 */ 141 private static class ThreadDumpMessageProxy implements Serializable { 142 143 private static final long serialVersionUID = -3476620450287648269L; 144 private final String formattedMsg; 145 private final String title; 146 147 ThreadDumpMessageProxy(final ThreadDumpMessage msg) { 148 this.formattedMsg = msg.getFormattedMessage(); 149 this.title = msg.title; 150 } 151 152 /** 153 * Returns a ThreadDumpMessage using the data in the proxy. 154 * @return a ThreadDumpMessage. 155 */ 156 protected Object readResolve() { 157 return new ThreadDumpMessage(formattedMsg, title); 158 } 159 } 160 161 /** 162 * Factory to create Thread information. 163 */ 164 private interface ThreadInfoFactory { 165 Map<ThreadInformation, StackTraceElement[]> createThreadInfo(); 166 } 167 168 /** 169 * Factory to create basic thread information. 170 */ 171 private static class BasicThreadInfoFactory implements ThreadInfoFactory { 172 @Override 173 public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() { 174 final Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces(); 175 final Map<ThreadInformation, StackTraceElement[]> threads = 176 new HashMap<>(map.size()); 177 for (final Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) { 178 threads.put(new BasicThreadInformation(entry.getKey()), entry.getValue()); 179 } 180 return threads; 181 } 182 } 183 184 /** 185 * Factory to create extended thread information. 186 */ 187 private static class ExtendedThreadInfoFactory implements ThreadInfoFactory { 188 @Override 189 public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() { 190 final ThreadMXBean bean = ManagementFactory.getThreadMXBean(); 191 final ThreadInfo[] array = bean.dumpAllThreads(true, true); 192 193 final Map<ThreadInformation, StackTraceElement[]> threads = 194 new HashMap<>(array.length); 195 for (final ThreadInfo info : array) { 196 threads.put(new ExtendedThreadInformation(info), info.getStackTrace()); 197 } 198 return threads; 199 } 200 } 201 202 /** 203 * Always returns null. 204 * 205 * @return null 206 */ 207 @Override 208 public Throwable getThrowable() { 209 return null; 210 } 211}