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