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