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  import org.apache.logging.log4j.util.StringBuilders;
    30  import org.apache.logging.log4j.util.Strings;
    31
    32  /**
    33   * Captures information about all running Threads.
    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       * Generate a ThreadDumpMessage with a title.
    61       * @param title The title.
    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       * Returns the ThreadDump in printable format.
    85       * @return the ThreadDump suitable for logging.
    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      * Returns the title.
    107      * @return the title.
    108      */
    109     @Override
    110     public String getFormat() {
    111         return title == null ? Strings.EMPTY : title;
    112     }
    113
    114     /**
    115      * Returns an array with a single element, a Map containing the ThreadInformation as the key.
    116      * and the StackTraceElement array as the value;
    117      * @return the "parameters" to this Message.
    118      */
    119     @Override
    120     public Object[] getParameters() {
    121         return null;
    122     }
    123
    124         /**
    125      * Creates a ThreadDumpMessageProxy that can be serialized.
    126      * @return a ThreadDumpMessageProxy.
    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      * Proxy pattern used to serialize the ThreadDumpMessage.
    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          * Returns a ThreadDumpMessage using the data in the proxy.
    153          * @return a ThreadDumpMessage.
    154          */
    155         protected Object readResolve() {
    156             return new ThreadDumpMessage(formattedMsg, title);
    157         }
    158     }
    159
    160     /**
    161      * Factory to create Thread information.
    162      */
    163     private interface ThreadInfoFactory {
    164         Map<ThreadInformation, StackTraceElement[]> createThreadInfo();
    165     }
    166
    167     /**
    168      * Factory to create basic thread information.
    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      * Factory to create extended thread information.
    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      * Always returns null.
    203      *
    204      * @return null
    205      */
    206     @Override
    207     public Throwable getThrowable() {
    208         return null;
    209     }
    210 }