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.util.HashMap;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.ServiceConfigurationError;
26 import java.util.ServiceLoader;
27
28 import org.apache.logging.log4j.status.StatusLogger;
29 import org.apache.logging.log4j.util.StringBuilderFormattable;
30 import org.apache.logging.log4j.util.Strings;
31
32
33
34
35 @AsynchronouslyFormattable
36 public class ThreadDumpMessage implements Message, StringBuilderFormattable {
37 private static final long serialVersionUID = -1103400781608841088L;
38 private static ThreadInfoFactory FACTORY;
39
40 private volatile Map<ThreadInformation, StackTraceElement[]> threads;
41 private final String title;
42 private String formattedMessage;
43
44
45
46
47
48 public ThreadDumpMessage(final String title) {
49 this.title = title == null ? Strings.EMPTY : title;
50 threads = getFactory().createThreadInfo();
51 }
52
53 private ThreadDumpMessage(final String formattedMsg, final String title) {
54 this.formattedMessage = formattedMsg;
55 this.title = title == null ? Strings.EMPTY : title;
56 }
57
58 private static ThreadInfoFactory getFactory() {
59 if (FACTORY == null) {
60 FACTORY = initFactory(ThreadDumpMessage.class.getClassLoader());
61 }
62 return FACTORY;
63 }
64
65 private static ThreadInfoFactory initFactory(final ClassLoader classLoader) {
66 final ServiceLoader<ThreadInfoFactory> serviceLoader = ServiceLoader.load(ThreadInfoFactory.class, classLoader);
67 ThreadInfoFactory result = null;
68 try {
69 final Iterator<ThreadInfoFactory> iterator = serviceLoader.iterator();
70 while (result == null && iterator.hasNext()) {
71 result = iterator.next();
72 }
73 } catch (ServiceConfigurationError | LinkageError | Exception unavailable) {
74 StatusLogger.getLogger().info("ThreadDumpMessage uses BasicThreadInfoFactory: " +
75 "could not load extended ThreadInfoFactory: {}", unavailable.toString());
76 result = null;
77 }
78 return result == null ? new BasicThreadInfoFactory() : result;
79 }
80
81 @Override
82 public String toString() {
83 return getFormattedMessage();
84 }
85
86
87
88
89
90 @Override
91 public String getFormattedMessage() {
92 if (formattedMessage != null) {
93 return formattedMessage;
94 }
95 final StringBuilder sb = new StringBuilder(255);
96 formatTo(sb);
97 return sb.toString();
98 }
99
100 @Override
101 public void formatTo(final StringBuilder sb) {
102 sb.append(title);
103 if (title.length() > 0) {
104 sb.append('\n');
105 }
106 for (final Map.Entry<ThreadInformation, StackTraceElement[]> entry : threads.entrySet()) {
107 final ThreadInformation info = entry.getKey();
108 info.printThreadInfo(sb);
109 info.printStack(sb, entry.getValue());
110 sb.append('\n');
111 }
112 }
113
114
115
116
117
118 @Override
119 public String getFormat() {
120 return title == null ? Strings.EMPTY : title;
121 }
122
123
124
125
126
127
128 @Override
129 public Object[] getParameters() {
130 return null;
131 }
132
133
134
135
136
137 protected Object writeReplace() {
138 return new ThreadDumpMessageProxy(this);
139 }
140
141 private void readObject(final ObjectInputStream stream)
142 throws InvalidObjectException {
143 throw new InvalidObjectException("Proxy required");
144 }
145
146
147
148
149 private static class ThreadDumpMessageProxy implements Serializable {
150
151 private static final long serialVersionUID = -3476620450287648269L;
152 private final String formattedMsg;
153 private final String title;
154
155 ThreadDumpMessageProxy(final ThreadDumpMessage msg) {
156 this.formattedMsg = msg.getFormattedMessage();
157 this.title = msg.title;
158 }
159
160
161
162
163
164 protected Object readResolve() {
165 return new ThreadDumpMessage(formattedMsg, title);
166 }
167 }
168
169
170
171
172
173
174
175
176 public static interface ThreadInfoFactory {
177 Map<ThreadInformation, StackTraceElement[]> createThreadInfo();
178 }
179
180
181
182
183 private static class BasicThreadInfoFactory implements ThreadInfoFactory {
184 @Override
185 public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() {
186 final Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
187 final Map<ThreadInformation, StackTraceElement[]> threads =
188 new HashMap<>(map.size());
189 for (final Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
190 threads.put(new BasicThreadInformation(entry.getKey()), entry.getValue());
191 }
192 return threads;
193 }
194 }
195
196
197
198
199
200
201 @Override
202 public Throwable getThrowable() {
203 return null;
204 }
205 }