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.Logger;
25  import org.apache.logging.log4j.core.Filter;
26  import org.apache.logging.log4j.core.LogEvent;
27  import org.apache.logging.log4j.core.LoggerContext;
28  import org.apache.logging.log4j.core.LoggerContext.Status;
29  import org.apache.logging.log4j.core.config.AppenderRef;
30  import org.apache.logging.log4j.core.config.Configuration;
31  import org.apache.logging.log4j.core.config.LoggerConfig;
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.PluginAttr;
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.status.StatusLogger;
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 FastFileAppender or
62   * FastRollingFileAppender, 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", type = "Core", printObject = true)
69  public class AsyncLoggerConfig extends LoggerConfig {
70  
71      private static final Logger LOGGER = StatusLogger.getLogger();
72      private AsyncLoggerConfigHelper helper;
73  
74      /**
75       * Default constructor.
76       */
77      public AsyncLoggerConfig() {
78          super();
79      }
80  
81      /**
82       * Constructor that sets the name, level and additive values.
83       * 
84       * @param name The Logger name.
85       * @param level The Level.
86       * @param additive true if the Logger is additive, false otherwise.
87       */
88      public AsyncLoggerConfig(final String name, final Level level,
89              final boolean additive) {
90          super(name, level, additive);
91      }
92  
93      protected AsyncLoggerConfig(final String name,
94              final List<AppenderRef> appenders, final Filter filter,
95              final Level level, final boolean additive,
96              final Property[] properties, final Configuration config,
97              final boolean includeLocation) {
98          super(name, appenders, filter, level, additive, properties, config,
99                  includeLocation);
100     }
101 
102     /**
103      * Passes on the event to a separate thread that will call
104      * {@link #asyncCallAppenders(LogEvent)}.
105      */
106     @Override
107     protected void callAppenders(LogEvent event) {
108         // populate lazily initialized fields
109         event.getSource();
110         event.getThreadName();
111 
112         // pass on the event to a separate thread
113         helper.callAppendersFromAnotherThread(event);
114     }
115 
116     /** Called by AsyncLoggerConfigHelper.RingBufferLog4jEventHandler. */
117     void asyncCallAppenders(LogEvent event) {
118         super.callAppenders(event);
119     }
120 
121     @Override
122     public void startFilter() {
123         if (helper == null) {
124             helper = new AsyncLoggerConfigHelper(this);
125         }
126         super.startFilter();
127     }
128 
129     @Override
130     public void stopFilter() {
131         // only stop disruptor if shutting down logging subsystem
132         if (LogManager.getContext() instanceof LoggerContext) {
133             if (((LoggerContext) LogManager.getContext()).getStatus() != Status.STOPPING) {
134                 return;
135             }
136         }
137         helper.shutdown();
138         super.stopFilter();
139     }
140 
141     /**
142      * Factory method to create a LoggerConfig.
143      * 
144      * @param additivity True if additive, false otherwise.
145      * @param levelName The Level to be associated with the Logger.
146      * @param loggerName The name of the Logger.
147      * @param includeLocation "true" if location should be passed downstream
148      * @param refs An array of Appender names.
149      * @param properties Properties to pass to the Logger.
150      * @param config The Configuration.
151      * @param filter A Filter.
152      * @return A new LoggerConfig.
153      */
154     @PluginFactory
155     public static LoggerConfig createLogger(
156             @PluginAttr("additivity") final String additivity,
157             @PluginAttr("level") final String levelName,
158             @PluginAttr("name") final String loggerName,
159             @PluginAttr("includeLocation") final String includeLocation,
160             @PluginElement("appender-ref") final AppenderRef[] refs,
161             @PluginElement("properties") final Property[] properties,
162             @PluginConfiguration final Configuration config,
163             @PluginElement("filters") final Filter filter) {
164         if (loggerName == null) {
165             LOGGER.error("Loggers cannot be configured without a name");
166             return null;
167         }
168 
169         final List<AppenderRef> appenderRefs = Arrays.asList(refs);
170         Level level;
171         try {
172             level = Level.toLevel(levelName, Level.ERROR);
173         } catch (final Exception ex) {
174             LOGGER.error(
175                     "Invalid Log level specified: {}. Defaulting to Error",
176                     levelName);
177             level = Level.ERROR;
178         }
179         final String name = loggerName.equals("root") ? "" : loggerName;
180         final boolean additive = additivity == null ? true : Boolean
181                 .parseBoolean(additivity);
182 
183         return new AsyncLoggerConfig(name, appenderRefs, filter, level,
184                 additive, properties, config, includeLocation(includeLocation));
185     }
186 
187     // Note: for asynchronous loggers, includeLocation default is FALSE
188     private static boolean includeLocation(String includeLocationConfigValue) {
189         if (includeLocationConfigValue == null) {
190             return false;
191         }
192         return Boolean.parseBoolean(includeLocationConfigValue);
193     }
194 
195     /**
196      * An asynchronous root Logger.
197      */
198     @Plugin(name = "asyncRoot", type = "Core", printObject = true)
199     public static class RootLogger extends LoggerConfig {
200 
201         @PluginFactory
202         public static LoggerConfig createLogger(
203                 @PluginAttr("additivity") final String additivity,
204                 @PluginAttr("level") final String levelName,
205                 @PluginAttr("includeLocation") final String includeLocation,
206                 @PluginElement("appender-ref") final AppenderRef[] refs,
207                 @PluginElement("properties") final Property[] properties,
208                 @PluginConfiguration final Configuration config,
209                 @PluginElement("filters") final Filter filter) {
210             final List<AppenderRef> appenderRefs = Arrays.asList(refs);
211             Level level;
212             try {
213                 level = Level.toLevel(levelName, Level.ERROR);
214             } catch (final Exception ex) {
215                 LOGGER.error(
216                         "Invalid Log level specified: {}. Defaulting to Error",
217                         levelName);
218                 level = Level.ERROR;
219             }
220             final boolean additive = additivity == null ? true : Boolean
221                     .parseBoolean(additivity);
222 
223             return new AsyncLoggerConfig(LogManager.ROOT_LOGGER_NAME,
224                     appenderRefs, filter, level, additive, properties, config,
225                     includeLocation(includeLocation));
226         }
227     }
228 }