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.StringBuilders;
030 import org.apache.logging.log4j.util.Strings;
031
032 /**
033 * Captures information about all running Threads.
034 */
035 public 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 }