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