View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
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  /**
30   * Captures information about all running Threads.
31   */
32  public class ThreadDumpMessage implements Message {
33  
34      private static final long serialVersionUID = -1103400781608841088L;
35  
36      private static final ThreadInfoFactory FACTORY;
37  
38      private volatile Map<ThreadInformation, StackTraceElement[]> threads;
39  
40      private final String title;
41  
42      private String formattedMessage;
43  
44      static {
45          final Method[] methods = ThreadInfo.class.getMethods();
46          boolean basic = true;
47          for (final Method method : methods) {
48              if (method.getName().equals("getLockInfo")) {
49                  basic = false;
50                  break;
51              }
52          }
53          FACTORY = basic ? new BasicThreadInfoFactory() : new ExtendedThreadInfoFactory();
54      }
55  
56      /**
57       * Generate a ThreadDumpMessage with a title.
58       * @param title The title.
59       */
60      public ThreadDumpMessage(final String title) {
61          this.title = title == null ? "" : title;
62          threads = FACTORY.createThreadInfo();
63      }
64  
65      private ThreadDumpMessage(final String formattedMsg, final String title) {
66          this.formattedMessage = formattedMsg;
67          this.title = title == null ? "" : title;
68      }
69  
70      @Override
71      public String toString() {
72          final StringBuilder sb = new StringBuilder("ThreadDumpMessage[");
73          if (this.title.length() > 0) {
74              sb.append("Title=\"").append(this.title).append("\"");
75          }
76          sb.append("]");
77          return sb.toString();
78      }
79  
80      /**
81       * Returns the ThreadDump in printable format.
82       * @return the ThreadDump suitable for logging.
83       */
84      @Override
85      public String getFormattedMessage() {
86          if (formattedMessage != null) {
87              return formattedMessage;
88          }
89          final StringBuilder sb = new StringBuilder(title);
90          if (title.length() > 0) {
91              sb.append("\n");
92          }
93          for (final Map.Entry<ThreadInformation, StackTraceElement[]> entry : threads.entrySet()) {
94              final ThreadInformation info = entry.getKey();
95              info.printThreadInfo(sb);
96              info.printStack(sb, entry.getValue());
97              sb.append("\n");
98          }
99          return sb.toString();
100     }
101 
102     /**
103      * Returns the title.
104      * @return the title.
105      */
106     @Override
107     public String getFormat() {
108         return title == null ? "" : title;
109     }
110 
111     /**
112      * Returns an array with a single element, a Map containing the ThreadInformation as the key.
113      * and the StackTraceElement array as the value;
114      * @return the "parameters" to this Message.
115      */
116     @Override
117     public Object[] getParameters() {
118         return null;
119     }
120 
121         /**
122      * Creates a ThreadDumpMessageProxy that can be serialized.
123      * @return a ThreadDumpMessageProxy.
124      */
125     protected Object writeReplace() {
126         return new ThreadDumpMessageProxy(this);
127     }
128 
129     private void readObject(final ObjectInputStream stream)
130         throws InvalidObjectException {
131         throw new InvalidObjectException("Proxy required");
132     }
133 
134     /**
135      * Proxy pattern used to serialize the ThreadDumpMessage.
136      */
137     private static class ThreadDumpMessageProxy implements Serializable {
138 
139         private static final long serialVersionUID = -3476620450287648269L;
140         private final String formattedMsg;
141         private final String title;
142 
143         public ThreadDumpMessageProxy(final ThreadDumpMessage msg) {
144             this.formattedMsg = msg.getFormattedMessage();
145             this.title = msg.title;
146         }
147 
148         /**
149          * Returns a ThreadDumpMessage using the data in the proxy.
150          * @return a ThreadDumpMessage.
151          */
152         protected Object readResolve() {
153             return new ThreadDumpMessage(formattedMsg, title);
154         }
155     }
156 
157     /**
158      * Factory to create Thread information.
159      */
160     private interface ThreadInfoFactory {
161         Map<ThreadInformation, StackTraceElement[]> createThreadInfo();
162     }
163 
164     /**
165      * Factory to create basic thread information.
166      */
167     private static class BasicThreadInfoFactory implements ThreadInfoFactory {
168         @Override
169         public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() {
170             final Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
171             final Map<ThreadInformation, StackTraceElement[]> threads =
172                 new HashMap<ThreadInformation, StackTraceElement[]>(map.size());
173             for (final Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
174                 threads.put(new BasicThreadInformation(entry.getKey()), entry.getValue());
175             }
176             return threads;
177         }
178     }
179 
180     /**
181      * Factory to create extended thread information.
182      */
183     private static class ExtendedThreadInfoFactory implements ThreadInfoFactory {
184         @Override
185         public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() {
186             final ThreadMXBean bean = ManagementFactory.getThreadMXBean();
187             final ThreadInfo[] array = bean.dumpAllThreads(true, true);
188 
189             final Map<ThreadInformation, StackTraceElement[]>  threads =
190                 new HashMap<ThreadInformation, StackTraceElement[]>(array.length);
191             for (final ThreadInfo info : array) {
192                 threads.put(new ExtendedThreadInformation(info), info.getStackTrace());
193             }
194             return threads;
195         }
196     }
197 
198     /**
199      * Always returns null.
200      *
201      * @return null
202      */
203     @Override
204     public Throwable getThrowable() {
205         return null;
206     }
207 }