1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.message;
18
19 import java.io.InvalidObjectException;
20 import java.io.ObjectInputStream;
21 import java.io.Serializable;
22 import java.lang.management.ManagementFactory;
23 import java.lang.management.ThreadInfo;
24 import java.lang.management.ThreadMXBean;
25 import java.lang.reflect.Method;
26 import java.util.HashMap;
27 import java.util.Map;
28
29 import org.apache.logging.log4j.util.StringBuilderFormattable;
30 import org.apache.logging.log4j.util.Strings;
31
32
33
34
35 public class ThreadDumpMessage implements Message, StringBuilderFormattable {
36
37 private static final long serialVersionUID = -1103400781608841088L;
38
39 private static final ThreadInfoFactory FACTORY;
40
41 private volatile Map<ThreadInformation, StackTraceElement[]> threads;
42
43 private final String title;
44
45 private String formattedMessage;
46
47 static {
48 final Method[] methods = ThreadInfo.class.getMethods();
49 boolean basic = true;
50 for (final Method method : methods) {
51 if (method.getName().equals("getLockInfo")) {
52 basic = false;
53 break;
54 }
55 }
56 FACTORY = basic ? new BasicThreadInfoFactory() : new ExtendedThreadInfoFactory();
57 }
58
59
60
61
62
63 public ThreadDumpMessage(final String title) {
64 this.title = title == null ? Strings.EMPTY : title;
65 threads = FACTORY.createThreadInfo();
66 }
67
68 private ThreadDumpMessage(final String formattedMsg, final String title) {
69 this.formattedMessage = formattedMsg;
70 this.title = title == null ? Strings.EMPTY : title;
71 }
72
73 @Override
74 public String toString() {
75 return getFormattedMessage();
76 }
77
78
79
80
81
82 @Override
83 public String getFormattedMessage() {
84 if (formattedMessage != null) {
85 return formattedMessage;
86 }
87 final StringBuilder sb = new StringBuilder(255);
88 formatTo(sb);
89 return sb.toString();
90 }
91
92 @Override
93 public void formatTo(final StringBuilder sb) {
94 sb.append(title);
95 if (title.length() > 0) {
96 sb.append('\n');
97 }
98 for (final Map.Entry<ThreadInformation, StackTraceElement[]> entry : threads.entrySet()) {
99 final ThreadInformation info = entry.getKey();
100 info.printThreadInfo(sb);
101 info.printStack(sb, entry.getValue());
102 sb.append('\n');
103 }
104 }
105
106
107
108
109
110 @Override
111 public String getFormat() {
112 return title == null ? Strings.EMPTY : title;
113 }
114
115
116
117
118
119
120 @Override
121 public Object[] getParameters() {
122 return null;
123 }
124
125
126
127
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
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
154
155
156 protected Object readResolve() {
157 return new ThreadDumpMessage(formattedMsg, title);
158 }
159 }
160
161
162
163
164 private interface ThreadInfoFactory {
165 Map<ThreadInformation, StackTraceElement[]> createThreadInfo();
166 }
167
168
169
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
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
204
205
206
207 @Override
208 public Throwable getThrowable() {
209 return null;
210 }
211 }