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.core.async;
18  
19  import java.util.Arrays;
20  import java.util.List;
21  import java.util.concurrent.TimeUnit;
22  
23  import org.apache.logging.log4j.Level;
24  import org.apache.logging.log4j.LogManager;
25  import org.apache.logging.log4j.core.Core;
26  import org.apache.logging.log4j.core.Filter;
27  import org.apache.logging.log4j.core.LogEvent;
28  import org.apache.logging.log4j.core.config.AppenderRef;
29  import org.apache.logging.log4j.core.config.Configuration;
30  import org.apache.logging.log4j.core.config.LoggerConfig;
31  import org.apache.logging.log4j.core.config.Node;
32  import org.apache.logging.log4j.core.config.Property;
33  import org.apache.logging.log4j.core.config.plugins.Plugin;
34  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
35  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
36  import org.apache.logging.log4j.core.config.plugins.PluginElement;
37  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
38  import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
39  import org.apache.logging.log4j.core.util.Booleans;
40  import org.apache.logging.log4j.util.Strings;
41  
42  /**
43   * Asynchronous Logger object that is created via configuration and can be
44   * combined with synchronous loggers.
45   * <p>
46   * AsyncLoggerConfig is a logger designed for high throughput and low latency
47   * logging. It does not perform any I/O in the calling (application) thread, but
48   * instead hands off the work to another thread as soon as possible. The actual
49   * logging is performed in the background thread. It uses the LMAX Disruptor
50   * library for inter-thread communication. (<a
51   * href="http://lmax-exchange.github.com/disruptor/"
52   * >http://lmax-exchange.github.com/disruptor/</a>)
53   * <p>
54   * To use AsyncLoggerConfig, specify {@code <asyncLogger>} or
55   * {@code <asyncRoot>} in configuration.
56   * <p>
57   * Note that for performance reasons, this logger does not include source
58   * location by default. You need to specify {@code includeLocation="true"} in
59   * the configuration or any %class, %location or %line conversion patterns in
60   * your log4j.xml configuration will produce either a "?" character or no output
61   * at all.
62   * <p>
63   * For best performance, use AsyncLoggerConfig with the RandomAccessFileAppender or
64   * RollingRandomAccessFileAppender, with immediateFlush=false. These appenders have
65   * built-in support for the batching mechanism used by the Disruptor library,
66   * and they will flush to disk at the end of each batch. This means that even
67   * with immediateFlush=false, there will never be any items left in the buffer;
68   * all log events will all be written to disk in a very efficient manner.
69   */
70  @Plugin(name = "asyncLogger", category = Node.CATEGORY, printObject = true)
71  public class AsyncLoggerConfig extends LoggerConfig {
72  
73      private final AsyncLoggerConfigDelegate delegate;
74  
75      protected AsyncLoggerConfig(final String name,
76              final List<AppenderRef> appenders, final Filter filter,
77              final Level level, final boolean additive,
78              final Property[] properties, final Configuration config,
79              final boolean includeLocation) {
80          super(name, appenders, filter, level, additive, properties, config,
81                  includeLocation);
82          delegate = config.getAsyncLoggerConfigDelegate();
83          delegate.setLogEventFactory(getLogEventFactory());
84      }
85  
86      /**
87       * Passes on the event to a separate thread that will call
88       * {@link #asyncCallAppenders(LogEvent)}.
89       */
90      @Override
91      protected void callAppenders(final LogEvent event) {
92          populateLazilyInitializedFields(event);
93  
94          if (!delegate.tryEnqueue(event, this)) {
95              final EventRoute eventRoute = delegate.getEventRoute(event.getLevel());
96              eventRoute.logMessage(this, event);
97          }
98      }
99  
100     private void populateLazilyInitializedFields(final LogEvent event) {
101         event.getSource();
102         event.getThreadName();
103     }
104 
105     void callAppendersInCurrentThread(final LogEvent event) {
106         super.callAppenders(event);
107     }
108 
109     void callAppendersInBackgroundThread(final LogEvent event) {
110         delegate.enqueueEvent(event, this);
111     }
112 
113     /** Called by AsyncLoggerConfigHelper.RingBufferLog4jEventHandler. */
114     void asyncCallAppenders(final LogEvent event) {
115         super.callAppenders(event);
116     }
117 
118     private String displayName() {
119         return LogManager.ROOT_LOGGER_NAME.equals(getName()) ? LoggerConfig.ROOT : getName();
120     }
121 
122     @Override
123     public void start() {
124         LOGGER.trace("AsyncLoggerConfig[{}] starting...", displayName());
125         super.start();
126     }
127 
128     @Override
129     public boolean stop(final long timeout, final TimeUnit timeUnit) {
130         setStopping();
131         super.stop(timeout, timeUnit, false);
132         LOGGER.trace("AsyncLoggerConfig[{}] stopping...", displayName());
133         setStopped();
134         return true;
135     }
136 
137     /**
138      * Creates and returns a new {@code RingBufferAdmin} that instruments the
139      * ringbuffer of this {@code AsyncLoggerConfig}.
140      *
141      * @param contextName name of the {@code LoggerContext}
142      * @return a new {@code RingBufferAdmin} that instruments the ringbuffer
143      */
144     public RingBufferAdmin createRingBufferAdmin(final String contextName) {
145         return delegate.createRingBufferAdmin(contextName, getName());
146     }
147 
148     /**
149      * Factory method to create a LoggerConfig.
150      *
151      * @param additivity True if additive, false otherwise.
152      * @param levelName The Level to be associated with the Logger.
153      * @param loggerName The name of the Logger.
154      * @param includeLocation "true" if location should be passed downstream
155      * @param refs An array of Appender names.
156      * @param properties Properties to pass to the Logger.
157      * @param config The Configuration.
158      * @param filter A Filter.
159      * @return A new LoggerConfig.
160      */
161     @PluginFactory
162     public static LoggerConfig createLogger(
163             @PluginAttribute("additivity") final String additivity,
164             @PluginAttribute("level") final String levelName,
165             @PluginAttribute("name") final String loggerName,
166             @PluginAttribute("includeLocation") final String includeLocation,
167             @PluginElement("AppenderRef") final AppenderRef[] refs,
168             @PluginElement("Properties") final Property[] properties,
169             @PluginConfiguration final Configuration config,
170             @PluginElement("Filter") final Filter filter) {
171         if (loggerName == null) {
172             LOGGER.error("Loggers cannot be configured without a name");
173             return null;
174         }
175 
176         final List<AppenderRef> appenderRefs = Arrays.asList(refs);
177         Level level;
178         try {
179             level = Level.toLevel(levelName, Level.ERROR);
180         } catch (final Exception ex) {
181             LOGGER.error(
182                     "Invalid Log level specified: {}. Defaulting to Error",
183                     levelName);
184             level = Level.ERROR;
185         }
186         final String name = loggerName.equals(LoggerConfig.ROOT) ? Strings.EMPTY : loggerName;
187         final boolean additive = Booleans.parseBoolean(additivity, true);
188 
189         return new AsyncLoggerConfig(name, appenderRefs, filter, level,
190                 additive, properties, config, includeLocation(includeLocation));
191     }
192 
193     // Note: for asynchronous loggers, includeLocation default is FALSE
194     protected static boolean includeLocation(final String includeLocationConfigValue) {
195         return Boolean.parseBoolean(includeLocationConfigValue);
196     }
197 
198     /**
199      * An asynchronous root Logger.
200      */
201     @Plugin(name = "asyncRoot", category = Core.CATEGORY_NAME, printObject = true)
202     public static class RootLogger extends LoggerConfig {
203 
204         @PluginFactory
205         public static LoggerConfig createLogger(
206                 @PluginAttribute("additivity") final String additivity,
207                 @PluginAttribute("level") final String levelName,
208                 @PluginAttribute("includeLocation") final String includeLocation,
209                 @PluginElement("AppenderRef") final AppenderRef[] refs,
210                 @PluginElement("Properties") final Property[] properties,
211                 @PluginConfiguration final Configuration config,
212                 @PluginElement("Filter") final Filter filter) {
213             final List<AppenderRef> appenderRefs = Arrays.asList(refs);
214             Level level;
215             try {
216                 level = Level.toLevel(levelName, Level.ERROR);
217             } catch (final Exception ex) {
218                 LOGGER.error(
219                         "Invalid Log level specified: {}. Defaulting to Error",
220                         levelName);
221                 level = Level.ERROR;
222             }
223             final boolean additive = Booleans.parseBoolean(additivity, true);
224 
225             return new AsyncLoggerConfig(LogManager.ROOT_LOGGER_NAME,
226                     appenderRefs, filter, level, additive, properties, config,
227                     AsyncLoggerConfig.includeLocation(includeLocation));
228         }
229     }
230 }