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