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  
18  package org.apache.logging.log4j.core.async;
19  
20  import java.util.concurrent.Callable;
21  import java.util.concurrent.ExecutorService;
22  import java.util.concurrent.Future;
23  
24  import org.apache.logging.log4j.Logger;
25  import org.apache.logging.log4j.core.util.Integers;
26  import org.apache.logging.log4j.status.StatusLogger;
27  import org.apache.logging.log4j.util.PropertiesUtil;
28  
29  import com.lmax.disruptor.BlockingWaitStrategy;
30  import com.lmax.disruptor.ExceptionHandler;
31  import com.lmax.disruptor.SleepingWaitStrategy;
32  import com.lmax.disruptor.WaitStrategy;
33  import com.lmax.disruptor.YieldingWaitStrategy;
34  
35  /**
36   * Utility methods for getting Disruptor related configuration.
37   */
38  final class DisruptorUtil {
39      private static final Logger LOGGER = StatusLogger.getLogger();
40      private static final int RINGBUFFER_MIN_SIZE = 128;
41      private static final int RINGBUFFER_DEFAULT_SIZE = 256 * 1024;
42  
43      private DisruptorUtil() {
44      }
45  
46      static WaitStrategy createWaitStrategy(final String propertyName) {
47          final String strategy = PropertiesUtil.getProperties().getStringProperty(propertyName);
48          if (strategy != null) {
49              LOGGER.trace("property {}={}", propertyName, strategy);
50              if ("Sleep".equalsIgnoreCase(strategy)) {
51                  return new SleepingWaitStrategy();
52              } else if ("Yield".equalsIgnoreCase(strategy)) {
53                  return new YieldingWaitStrategy();
54              } else if ("Block".equalsIgnoreCase(strategy)) {
55                  return new BlockingWaitStrategy();
56              }
57          }
58          return new BlockingWaitStrategy();
59      }
60  
61      static int calculateRingBufferSize(final String propertyName) {
62          int ringBufferSize = RINGBUFFER_DEFAULT_SIZE;
63          final String userPreferredRBSize = PropertiesUtil.getProperties().getStringProperty(propertyName,
64                  String.valueOf(ringBufferSize));
65          try {
66              int size = Integer.parseInt(userPreferredRBSize);
67              if (size < RINGBUFFER_MIN_SIZE) {
68                  size = RINGBUFFER_MIN_SIZE;
69                  LOGGER.warn("Invalid RingBufferSize {}, using minimum size {}.", userPreferredRBSize,
70                          RINGBUFFER_MIN_SIZE);
71              }
72              ringBufferSize = size;
73          } catch (final Exception ex) {
74              LOGGER.warn("Invalid RingBufferSize {}, using default size {}.", userPreferredRBSize, ringBufferSize);
75          }
76          return Integers.ceilingNextPowerOfTwo(ringBufferSize);
77      }
78  
79      static <T> ExceptionHandler<T> getExceptionHandler(final String propertyName, Class<T> type) {
80          final String cls = PropertiesUtil.getProperties().getStringProperty(propertyName);
81          if (cls == null) {
82              return null;
83          }
84          try {
85              @SuppressWarnings("unchecked")
86              final Class<? extends ExceptionHandler<T>> klass = (Class<? extends ExceptionHandler<T>>) Class
87                      .forName(cls);
88              return klass.newInstance();
89          } catch (final Exception ignored) {
90              LOGGER.debug("Invalid {} value: error creating {}: ", propertyName, cls, ignored);
91              return null;
92          }
93      }
94  
95      /**
96       * Returns the thread ID of the background appender thread. This allows us to detect Logger.log() calls initiated
97       * from the appender thread, which may cause deadlock when the RingBuffer is full. (LOG4J2-471)
98       * 
99       * @param executor runs the appender thread
100      * @return the thread ID of the background appender thread
101      */
102     public static long getExecutorThreadId(final ExecutorService executor) {
103         Future<Long> result = executor.submit(new Callable<Long>() {
104             @Override
105             public Long call() {
106                 return Thread.currentThread().getId();
107             }
108         });
109         try {
110             return result.get();
111         } catch (final Exception ex) {
112             final String msg = "Could not obtain executor thread Id. "
113                     + "Giving up to avoid the risk of application deadlock.";
114             throw new IllegalStateException(msg, ex);
115         }
116     }
117 
118 }