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 */ 017package org.apache.logging.log4j.core.async; 018 019import java.util.Arrays; 020import java.util.List; 021 022import org.apache.logging.log4j.Level; 023import org.apache.logging.log4j.LogManager; 024import org.apache.logging.log4j.core.Filter; 025import org.apache.logging.log4j.core.LogEvent; 026import org.apache.logging.log4j.core.config.AppenderRef; 027import org.apache.logging.log4j.core.config.Configuration; 028import org.apache.logging.log4j.core.config.LoggerConfig; 029import org.apache.logging.log4j.core.config.Node; 030import org.apache.logging.log4j.core.config.Property; 031import org.apache.logging.log4j.core.config.plugins.Plugin; 032import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 033import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 034import org.apache.logging.log4j.core.config.plugins.PluginElement; 035import org.apache.logging.log4j.core.config.plugins.PluginFactory; 036import org.apache.logging.log4j.core.jmx.RingBufferAdmin; 037import org.apache.logging.log4j.core.util.Booleans; 038import 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) 069public 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}