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