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.Locale;
21  import java.util.concurrent.Callable;
22  import java.util.concurrent.ExecutorService;
23  import java.util.concurrent.Future;
24  import java.util.concurrent.TimeUnit;
25  
26  import com.lmax.disruptor.BlockingWaitStrategy;
27  import com.lmax.disruptor.BusySpinWaitStrategy;
28  import com.lmax.disruptor.ExceptionHandler;
29  import com.lmax.disruptor.SleepingWaitStrategy;
30  import com.lmax.disruptor.TimeoutBlockingWaitStrategy;
31  import com.lmax.disruptor.WaitStrategy;
32  import com.lmax.disruptor.YieldingWaitStrategy;
33  import org.apache.logging.log4j.Logger;
34  import org.apache.logging.log4j.core.util.Constants;
35  import org.apache.logging.log4j.core.util.Integers;
36  import org.apache.logging.log4j.core.util.Loader;
37  import org.apache.logging.log4j.status.StatusLogger;
38  import org.apache.logging.log4j.util.PropertiesUtil;
39  
40  /**
41   * Utility methods for getting Disruptor related configuration.
42   */
43  final class DisruptorUtil {
44      private static final Logger LOGGER = StatusLogger.getLogger();
45      private static final int RINGBUFFER_MIN_SIZE = 128;
46      private static final int RINGBUFFER_DEFAULT_SIZE = 256 * 1024;
47      private static final int RINGBUFFER_NO_GC_DEFAULT_SIZE = 4 * 1024;
48  
49      private DisruptorUtil() {
50      }
51  
52      static long getTimeout(final String propertyName, final long defaultTimeout) {
53          return PropertiesUtil.getProperties().getLongProperty(propertyName, defaultTimeout);
54      }
55  
56      static WaitStrategy createWaitStrategy(final String propertyName) {
57          final String key = propertyName.startsWith("AsyncLogger.")
58                  ? "AsyncLogger.Timeout"
59                  : "AsyncLoggerConfig.Timeout";
60          final long timeoutMillis = DisruptorUtil.getTimeout(key, 10L);
61          return createWaitStrategy(propertyName, timeoutMillis);
62      }
63  
64      static WaitStrategy createWaitStrategy(final String propertyName, final long timeoutMillis) {
65          final String strategy = PropertiesUtil.getProperties().getStringProperty(propertyName, "TIMEOUT");
66          LOGGER.trace("property {}={}", propertyName, strategy);
67          final String strategyUp = strategy.toUpperCase(Locale.ROOT); // TODO Refactor into Strings.toRootUpperCase(String)
68          switch (strategyUp) { // TODO Define a DisruptorWaitStrategy enum?
69          case "SLEEP":
70              return new SleepingWaitStrategy();
71          case "YIELD":
72              return new YieldingWaitStrategy();
73          case "BLOCK":
74              return new BlockingWaitStrategy();
75          case "BUSYSPIN":
76              return new BusySpinWaitStrategy();
77          case "TIMEOUT":
78              return new TimeoutBlockingWaitStrategy(timeoutMillis, TimeUnit.MILLISECONDS);
79          default:
80              return new TimeoutBlockingWaitStrategy(timeoutMillis, TimeUnit.MILLISECONDS);
81          }
82      }
83  
84      static int calculateRingBufferSize(final String propertyName) {
85          int ringBufferSize = Constants.ENABLE_THREADLOCALS ? RINGBUFFER_NO_GC_DEFAULT_SIZE : RINGBUFFER_DEFAULT_SIZE;
86          final String userPreferredRBSize = PropertiesUtil.getProperties().getStringProperty(propertyName,
87                  String.valueOf(ringBufferSize));
88          try {
89              int size = Integer.parseInt(userPreferredRBSize);
90              if (size < RINGBUFFER_MIN_SIZE) {
91                  size = RINGBUFFER_MIN_SIZE;
92                  LOGGER.warn("Invalid RingBufferSize {}, using minimum size {}.", userPreferredRBSize,
93                          RINGBUFFER_MIN_SIZE);
94              }
95              ringBufferSize = size;
96          } catch (final Exception ex) {
97              LOGGER.warn("Invalid RingBufferSize {}, using default size {}.", userPreferredRBSize, ringBufferSize);
98          }
99          return Integers.ceilingNextPowerOfTwo(ringBufferSize);
100     }
101 
102     static ExceptionHandler<RingBufferLogEvent> getAsyncLoggerExceptionHandler() {
103         final String cls = PropertiesUtil.getProperties().getStringProperty("AsyncLogger.ExceptionHandler");
104         if (cls == null) {
105             return new AsyncLoggerDefaultExceptionHandler();
106         }
107         try {
108             @SuppressWarnings("unchecked")
109             final Class<? extends ExceptionHandler<RingBufferLogEvent>> klass =
110                 (Class<? extends ExceptionHandler<RingBufferLogEvent>>) Loader.loadClass(cls);
111             return klass.newInstance();
112         } catch (final Exception ignored) {
113             LOGGER.debug("Invalid AsyncLogger.ExceptionHandler value: error creating {}: ", cls, ignored);
114             return new AsyncLoggerDefaultExceptionHandler();
115         }
116     }
117 
118     static ExceptionHandler<AsyncLoggerConfigDisruptor.Log4jEventWrapper> getAsyncLoggerConfigExceptionHandler() {
119         final String cls = PropertiesUtil.getProperties().getStringProperty("AsyncLoggerConfig.ExceptionHandler");
120         if (cls == null) {
121             return new AsyncLoggerConfigDefaultExceptionHandler();
122         }
123         try {
124             @SuppressWarnings("unchecked")
125             final Class<? extends ExceptionHandler<AsyncLoggerConfigDisruptor.Log4jEventWrapper>> klass =
126                     (Class<? extends ExceptionHandler<AsyncLoggerConfigDisruptor.Log4jEventWrapper>>) Loader.loadClass(cls);
127             return klass.newInstance();
128         } catch (final Exception ignored) {
129             LOGGER.debug("Invalid AsyncLoggerConfig.ExceptionHandler value: error creating {}: ", cls, ignored);
130             return new AsyncLoggerConfigDefaultExceptionHandler();
131         }
132     }
133 
134     /**
135      * Returns the thread ID of the background appender thread. This allows us to detect Logger.log() calls initiated
136      * from the appender thread, which may cause deadlock when the RingBuffer is full. (LOG4J2-471)
137      *
138      * @param executor runs the appender thread
139      * @return the thread ID of the background appender thread
140      */
141     public static long getExecutorThreadId(final ExecutorService executor) {
142         final Future<Long> result = executor.submit(new Callable<Long>() {
143             @Override
144             public Long call() {
145                 return Thread.currentThread().getId();
146             }
147         });
148         try {
149             return result.get();
150         } catch (final Exception ex) {
151             final String msg = "Could not obtain executor thread Id. "
152                     + "Giving up to avoid the risk of application deadlock.";
153             throw new IllegalStateException(msg, ex);
154         }
155     }
156 }