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 /** 030 * Captures information about all running Threads. 031 */ 032 public class ThreadDumpMessage implements Message { 033 034 private static final long serialVersionUID = -1103400781608841088L; 035 036 private final static 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 Method[] methods = ThreadInfo.class.getMethods(); 046 boolean basic = true; 047 for (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(String title) { 061 this.title = title == null ? "" : title; 062 threads = factory.createThreadInfo(); 063 } 064 065 private ThreadDumpMessage(String formattedMsg, String title) { 066 this.formattedMessage = formattedMsg; 067 this.title = title == null ? "" : title; 068 } 069 070 @Override 071 public String toString() { 072 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 public String getFormattedMessage() { 085 if (formattedMessage != null) { 086 return formattedMessage; 087 } 088 StringBuilder sb = new StringBuilder(title); 089 if (title.length() > 0) { 090 sb.append("\n"); 091 } 092 for (Map.Entry<ThreadInformation, StackTraceElement[]> entry : threads.entrySet()) { 093 ThreadInformation info = entry.getKey(); 094 info.printThreadInfo(sb); 095 info.printStack(sb, entry.getValue()); 096 sb.append("\n"); 097 } 098 return sb.toString(); 099 } 100 101 /** 102 * Returns the title. 103 * @return the title. 104 */ 105 public String getFormat() { 106 return title == null ? "" : title; 107 } 108 109 /** 110 * Returns an array with a single element, a Map containing the ThreadInformation as the key. 111 * and the StackTraceElement array as the value; 112 * @return the "parameters" to this Message. 113 */ 114 public Object[] getParameters() { 115 return null; 116 } 117 118 /** 119 * Creates a ThreadDumpMessageProxy that can be serialized. 120 * @return a ThreadDumpMessageProxy. 121 */ 122 protected Object writeReplace() { 123 return new ThreadDumpMessageProxy(this); 124 } 125 126 private void readObject(ObjectInputStream stream) 127 throws InvalidObjectException { 128 throw new InvalidObjectException("Proxy required"); 129 } 130 131 /** 132 * Proxy pattern used to serialize the ThreadDumpMessage. 133 */ 134 private static class ThreadDumpMessageProxy implements Serializable { 135 136 private static final long serialVersionUID = -3476620450287648269L; 137 private String formattedMsg; 138 private String title; 139 140 public ThreadDumpMessageProxy(ThreadDumpMessage msg) { 141 this.formattedMsg = msg.getFormattedMessage(); 142 this.title = msg.title; 143 } 144 145 /** 146 * Returns a ThreadDumpMessage using the data in the proxy. 147 * @return a ThreadDumpMessage. 148 */ 149 protected Object readResolve() { 150 return new ThreadDumpMessage(formattedMsg, title); 151 } 152 } 153 154 /** 155 * Factory to create Thread information. 156 */ 157 private interface ThreadInfoFactory { 158 Map<ThreadInformation, StackTraceElement[]> createThreadInfo(); 159 } 160 161 /** 162 * Factory to create basic thread information. 163 */ 164 private static class BasicThreadInfoFactory implements ThreadInfoFactory { 165 public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() { 166 Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces(); 167 Map<ThreadInformation, StackTraceElement[]> threads = 168 new HashMap<ThreadInformation, StackTraceElement[]>(map.size()); 169 for (Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) { 170 threads.put(new BasicThreadInformation(entry.getKey()), entry.getValue()); 171 } 172 return threads; 173 } 174 } 175 176 /** 177 * Factory to create extended thread information. 178 */ 179 private static class ExtendedThreadInfoFactory implements ThreadInfoFactory { 180 public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() { 181 ThreadMXBean bean = ManagementFactory.getThreadMXBean(); 182 ThreadInfo[] array = bean.dumpAllThreads(true, true); 183 184 Map<ThreadInformation, StackTraceElement[]> threads = 185 new HashMap<ThreadInformation, StackTraceElement[]>(array.length); 186 for (ThreadInfo info : array) { 187 threads.put(new ExtendedThreadInformation(info), info.getStackTrace()); 188 } 189 return threads; 190 } 191 } 192 }