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.Strings;
30
31
32
33
34 public class ThreadDumpMessage implements Message {
35
36 private static final long serialVersionUID = -1103400781608841088L;
37
38 private static final ThreadInfoFactory FACTORY;
39
40 private volatile Map<ThreadInformation, StackTraceElement[]> threads;
41
42 private final String title;
43
44 private String formattedMessage;
45
46 static {
47 final Method[] methods = ThreadInfo.class.getMethods();
48 boolean basic = true;
49 for (final Method method : methods) {
50 if (method.getName().equals("getLockInfo")) {
51 basic = false;
52 break;
53 }
54 }
55 FACTORY = basic ? new BasicThreadInfoFactory() : new ExtendedThreadInfoFactory();
56 }
57
58
59
60
61
62 public ThreadDumpMessage(final String title) {
63 this.title = title == null ? Strings.EMPTY : title;
64 threads = FACTORY.createThreadInfo();
65 }
66
67 private ThreadDumpMessage(final String formattedMsg, final String title) {
68 this.formattedMessage = formattedMsg;
69 this.title = title == null ? Strings.EMPTY : title;
70 }
71
72 @Override
73 public String toString() {
74 final StringBuilder sb = new StringBuilder("ThreadDumpMessage[");
75 if (this.title.length() > 0) {
76 sb.append("Title=\"").append(this.title).append('"');
77 }
78 sb.append(']');
79 return sb.toString();
80 }
81
82
83
84
85
86 @Override
87 public String getFormattedMessage() {
88 if (formattedMessage != null) {
89 return formattedMessage;
90 }
91 final StringBuilder sb = new StringBuilder(title);
92 if (title.length() > 0) {
93 sb.append('\n');
94 }
95 for (final Map.Entry<ThreadInformation, StackTraceElement[]> entry : threads.entrySet()) {
96 final ThreadInformation info = entry.getKey();
97 info.printThreadInfo(sb);
98 info.printStack(sb, entry.getValue());
99 sb.append('\n');
100 }
101 return sb.toString();
102 }
103
104
105
106
107
108 @Override
109 public String getFormat() {
110 return title == null ? Strings.EMPTY : title;
111 }
112
113
114
115
116
117
118 @Override
119 public Object[] getParameters() {
120 return null;
121 }
122
123
124
125
126
127 protected Object writeReplace() {
128 return new ThreadDumpMessageProxy(this);
129 }
130
131 private void readObject(final ObjectInputStream stream)
132 throws InvalidObjectException {
133 throw new InvalidObjectException("Proxy required");
134 }
135
136
137
138
139 private static class ThreadDumpMessageProxy implements Serializable {
140
141 private static final long serialVersionUID = -3476620450287648269L;
142 private final String formattedMsg;
143 private final String title;
144
145 public ThreadDumpMessageProxy(final ThreadDumpMessage msg) {
146 this.formattedMsg = msg.getFormattedMessage();
147 this.title = msg.title;
148 }
149
150
151
152
153
154 protected Object readResolve() {
155 return new ThreadDumpMessage(formattedMsg, title);
156 }
157 }
158
159
160
161
162 private interface ThreadInfoFactory {
163 Map<ThreadInformation, StackTraceElement[]> createThreadInfo();
164 }
165
166
167
168
169 private static class BasicThreadInfoFactory implements ThreadInfoFactory {
170 @Override
171 public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() {
172 final Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
173 final Map<ThreadInformation, StackTraceElement[]> threads =
174 new HashMap<ThreadInformation, StackTraceElement[]>(map.size());
175 for (final Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
176 threads.put(new BasicThreadInformation(entry.getKey()), entry.getValue());
177 }
178 return threads;
179 }
180 }
181
182
183
184
185 private static class ExtendedThreadInfoFactory implements ThreadInfoFactory {
186 @Override
187 public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() {
188 final ThreadMXBean bean = ManagementFactory.getThreadMXBean();
189 final ThreadInfo[] array = bean.dumpAllThreads(true, true);
190
191 final Map<ThreadInformation, StackTraceElement[]> threads =
192 new HashMap<ThreadInformation, StackTraceElement[]>(array.length);
193 for (final ThreadInfo info : array) {
194 threads.put(new ExtendedThreadInformation(info), info.getStackTrace());
195 }
196 return threads;
197 }
198 }
199
200
201
202
203
204
205 @Override
206 public Throwable getThrowable() {
207 return null;
208 }
209 }