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