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.StringBuilders;
30 import org.apache.logging.log4j.util.Strings;
31
32
33
34
35 public class ThreadDumpMessage implements Message {
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 final StringBuilder sb = new StringBuilder("ThreadDumpMessage[");
76 if (this.title.length() > 0) {
77 StringBuilders.appendKeyDqValue(sb, "Title", this.title);
78 }
79 sb.append(']');
80 return sb.toString();
81 }
82
83
84
85
86
87 @Override
88 public String getFormattedMessage() {
89 if (formattedMessage != null) {
90 return formattedMessage;
91 }
92 final StringBuilder sb = new StringBuilder(title);
93 if (title.length() > 0) {
94 sb.append('\n');
95 }
96 for (final Map.Entry<ThreadInformation, StackTraceElement[]> entry : threads.entrySet()) {
97 final ThreadInformation info = entry.getKey();
98 info.printThreadInfo(sb);
99 info.printStack(sb, entry.getValue());
100 sb.append('\n');
101 }
102 return sb.toString();
103 }
104
105
106
107
108
109 @Override
110 public String getFormat() {
111 return title == null ? Strings.EMPTY : title;
112 }
113
114
115
116
117
118
119 @Override
120 public Object[] getParameters() {
121 return null;
122 }
123
124
125
126
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
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
153
154
155 protected Object readResolve() {
156 return new ThreadDumpMessage(formattedMsg, title);
157 }
158 }
159
160
161
162
163 private interface ThreadInfoFactory {
164 Map<ThreadInformation, StackTraceElement[]> createThreadInfo();
165 }
166
167
168
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
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
203
204
205
206 @Override
207 public Throwable getThrowable() {
208 return null;
209 }
210 }