001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017 package org.apache.logging.log4j.core.async;
018
019 import java.util.Arrays;
020 import java.util.List;
021
022 import org.apache.logging.log4j.Level;
023 import org.apache.logging.log4j.LogManager;
024 import org.apache.logging.log4j.core.Filter;
025 import org.apache.logging.log4j.core.LogEvent;
026 import org.apache.logging.log4j.core.config.AppenderRef;
027 import org.apache.logging.log4j.core.config.Configuration;
028 import org.apache.logging.log4j.core.config.LoggerConfig;
029 import org.apache.logging.log4j.core.config.Node;
030 import org.apache.logging.log4j.core.config.Property;
031 import org.apache.logging.log4j.core.config.plugins.Plugin;
032 import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
033 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
034 import org.apache.logging.log4j.core.config.plugins.PluginElement;
035 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
036 import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
037 import org.apache.logging.log4j.core.util.Booleans;
038 import org.apache.logging.log4j.util.Strings;
039
040 /**
041 * Asynchronous Logger object that is created via configuration and can be
042 * combined with synchronous loggers.
043 * <p>
044 * AsyncLoggerConfig is a logger designed for high throughput and low latency
045 * logging. It does not perform any I/O in the calling (application) thread, but
046 * instead hands off the work to another thread as soon as possible. The actual
047 * logging is performed in the background thread. It uses the LMAX Disruptor
048 * library for inter-thread communication. (<a
049 * href="http://lmax-exchange.github.com/disruptor/"
050 * >http://lmax-exchange.github.com/disruptor/</a>)
051 * <p>
052 * To use AsyncLoggerConfig, specify {@code <asyncLogger>} or
053 * {@code <asyncRoot>} in configuration.
054 * <p>
055 * Note that for performance reasons, this logger does not include source
056 * location by default. You need to specify {@code includeLocation="true"} in
057 * the configuration or any %class, %location or %line conversion patterns in
058 * your log4j.xml configuration will produce either a "?" character or no output
059 * at all.
060 * <p>
061 * For best performance, use AsyncLoggerConfig with the RandomAccessFileAppender or
062 * RollingRandomAccessFileAppender, with immediateFlush=false. These appenders have
063 * built-in support for the batching mechanism used by the Disruptor library,
064 * and they will flush to disk at the end of each batch. This means that even
065 * with immediateFlush=false, there will never be any items left in the buffer;
066 * all log events will all be written to disk in a very efficient manner.
067 */
068 @Plugin(name = "asyncLogger", category = Node.CATEGORY, printObject = true)
069 public class AsyncLoggerConfig extends LoggerConfig {
070
071 private static final long serialVersionUID = 1L;
072
073 private AsyncLoggerConfigHelper helper;
074
075 /**
076 * Default constructor.
077 */
078 public AsyncLoggerConfig() {
079 super();
080 }
081
082 /**
083 * Constructor that sets the name, level and additive values.
084 *
085 * @param name The Logger name.
086 * @param level The Level.
087 * @param additive true if the Logger is additive, false otherwise.
088 */
089 public AsyncLoggerConfig(final String name, final Level level,
090 final boolean additive) {
091 super(name, level, additive);
092 }
093
094 protected AsyncLoggerConfig(final String name,
095 final List<AppenderRef> appenders, final Filter filter,
096 final Level level, final boolean additive,
097 final Property[] properties, final Configuration config,
098 final boolean includeLocation) {
099 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()) ? "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 */
154 public RingBufferAdmin createRingBufferAdmin(final String contextName) {
155 return helper.createRingBufferAdmin(contextName, getName());
156 }
157
158 /**
159 * Factory method to create a LoggerConfig.
160 *
161 * @param additivity True if additive, false otherwise.
162 * @param levelName The Level to be associated with the Logger.
163 * @param loggerName The name of the Logger.
164 * @param includeLocation "true" if location should be passed downstream
165 * @param refs An array of Appender names.
166 * @param properties Properties to pass to the Logger.
167 * @param config The Configuration.
168 * @param filter A Filter.
169 * @return A new LoggerConfig.
170 */
171 @PluginFactory
172 public static LoggerConfig createLogger(
173 @PluginAttribute("additivity") final String additivity,
174 @PluginAttribute("level") final String levelName,
175 @PluginAttribute("name") final String loggerName,
176 @PluginAttribute("includeLocation") final String includeLocation,
177 @PluginElement("AppenderRef") final AppenderRef[] refs,
178 @PluginElement("Properties") final Property[] properties,
179 @PluginConfiguration final Configuration config,
180 @PluginElement("Filter") final Filter filter) {
181 if (loggerName == null) {
182 LOGGER.error("Loggers cannot be configured without a name");
183 return null;
184 }
185
186 final List<AppenderRef> appenderRefs = Arrays.asList(refs);
187 Level level;
188 try {
189 level = Level.toLevel(levelName, Level.ERROR);
190 } catch (final Exception ex) {
191 LOGGER.error(
192 "Invalid Log level specified: {}. Defaulting to Error",
193 levelName);
194 level = Level.ERROR;
195 }
196 final String name = loggerName.equals("root") ? Strings.EMPTY : loggerName;
197 final boolean additive = Booleans.parseBoolean(additivity, true);
198
199 return new AsyncLoggerConfig(name, appenderRefs, filter, level,
200 additive, properties, config, includeLocation(includeLocation));
201 }
202
203 // Note: for asynchronous loggers, includeLocation default is FALSE
204 protected static boolean includeLocation(final String includeLocationConfigValue) {
205 return Boolean.parseBoolean(includeLocationConfigValue);
206 }
207
208 /**
209 * An asynchronous root Logger.
210 */
211 @Plugin(name = "asyncRoot", category = "Core", printObject = true)
212 public static class RootLogger extends LoggerConfig {
213
214 private static final long serialVersionUID = 1L;
215
216 @PluginFactory
217 public static LoggerConfig createLogger(
218 @PluginAttribute("additivity") final String additivity,
219 @PluginAttribute("level") final String levelName,
220 @PluginAttribute("includeLocation") final String includeLocation,
221 @PluginElement("AppenderRef") final AppenderRef[] refs,
222 @PluginElement("Properties") final Property[] properties,
223 @PluginConfiguration final Configuration config,
224 @PluginElement("Filter") final Filter filter) {
225 final List<AppenderRef> appenderRefs = Arrays.asList(refs);
226 Level level;
227 try {
228 level = Level.toLevel(levelName, Level.ERROR);
229 } catch (final Exception ex) {
230 LOGGER.error(
231 "Invalid Log level specified: {}. Defaulting to Error",
232 levelName);
233 level = Level.ERROR;
234 }
235 final boolean additive = Booleans.parseBoolean(additivity, true);
236
237 return new AsyncLoggerConfig(LogManager.ROOT_LOGGER_NAME,
238 appenderRefs, filter, level, additive, properties, config,
239 AsyncLoggerConfig.includeLocation(includeLocation));
240 }
241 }
242 }