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.Property; 030 import org.apache.logging.log4j.core.config.plugins.Plugin; 031 import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 032 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 033 import org.apache.logging.log4j.core.config.plugins.PluginElement; 034 import org.apache.logging.log4j.core.config.plugins.PluginFactory; 035 import org.apache.logging.log4j.core.jmx.RingBufferAdmin; 036 import org.apache.logging.log4j.core.util.Booleans; 037 import org.apache.logging.log4j.util.Strings; 038 039 /** 040 * Asynchronous Logger object that is created via configuration and can be 041 * combined with synchronous loggers. 042 * <p> 043 * AsyncLoggerConfig is a logger designed for high throughput and low latency 044 * logging. It does not perform any I/O in the calling (application) thread, but 045 * instead hands off the work to another thread as soon as possible. The actual 046 * logging is performed in the background thread. It uses the LMAX Disruptor 047 * library for inter-thread communication. (<a 048 * href="http://lmax-exchange.github.com/disruptor/" 049 * >http://lmax-exchange.github.com/disruptor/</a>) 050 * <p> 051 * To use AsyncLoggerConfig, specify {@code <asyncLogger>} or 052 * {@code <asyncRoot>} in configuration. 053 * <p> 054 * Note that for performance reasons, this logger does not include source 055 * location by default. You need to specify {@code includeLocation="true"} in 056 * the configuration or any %class, %location or %line conversion patterns in 057 * your log4j.xml configuration will produce either a "?" character or no output 058 * at all. 059 * <p> 060 * For best performance, use AsyncLoggerConfig with the RandomAccessFileAppender or 061 * RollingRandomAccessFileAppender, with immediateFlush=false. These appenders have 062 * built-in support for the batching mechanism used by the Disruptor library, 063 * and they will flush to disk at the end of each batch. This means that even 064 * with immediateFlush=false, there will never be any items left in the buffer; 065 * all log events will all be written to disk in a very efficient manner. 066 */ 067 @Plugin(name = "asyncLogger", category = "Core", printObject = true) 068 public class AsyncLoggerConfig extends LoggerConfig { 069 070 private AsyncLoggerConfigHelper helper; 071 072 /** 073 * Default constructor. 074 */ 075 public AsyncLoggerConfig() { 076 super(); 077 } 078 079 /** 080 * Constructor that sets the name, level and additive values. 081 * 082 * @param name The Logger name. 083 * @param level The Level. 084 * @param additive true if the Logger is additive, false otherwise. 085 */ 086 public AsyncLoggerConfig(final String name, final Level level, 087 final boolean additive) { 088 super(name, level, additive); 089 } 090 091 protected AsyncLoggerConfig(final String name, 092 final List<AppenderRef> appenders, final Filter filter, 093 final Level level, final boolean additive, 094 final Property[] properties, final Configuration config, 095 final boolean includeLocation) { 096 super(name, appenders, filter, level, additive, properties, config, 097 includeLocation); 098 } 099 100 /** 101 * Passes on the event to a separate thread that will call 102 * {@link #asyncCallAppenders(LogEvent)}. 103 */ 104 @Override 105 protected void callAppenders(final LogEvent event) { 106 // populate lazily initialized fields 107 event.getSource(); 108 event.getThreadName(); 109 110 // pass on the event to a separate thread 111 if (!helper.callAppendersFromAnotherThread(event)) { 112 super.callAppenders(event); 113 } 114 } 115 116 /** Called by AsyncLoggerConfigHelper.RingBufferLog4jEventHandler. */ 117 void asyncCallAppenders(final LogEvent event) { 118 super.callAppenders(event); 119 } 120 121 @Override 122 public void start() { 123 LOGGER.trace("AsyncLoggerConfig[{}] starting...", getName()); 124 this.setStarting(); 125 if (helper == null) { 126 helper = new AsyncLoggerConfigHelper(this); 127 } else { 128 AsyncLoggerConfigHelper.claim(); // LOG4J2-336 129 } 130 super.start(); 131 } 132 133 @Override 134 public void stop() { 135 LOGGER.trace("AsyncLoggerConfig[{}] stopping...", getName()); 136 this.setStopping(); 137 AsyncLoggerConfigHelper.release(); 138 super.stop(); 139 } 140 141 /** 142 * Creates and returns a new {@code RingBufferAdmin} that instruments the 143 * ringbuffer of this {@code AsyncLoggerConfig}. 144 * 145 * @param contextName name of the {@code LoggerContext} 146 */ 147 public RingBufferAdmin createRingBufferAdmin(String contextName) { 148 return helper.createRingBufferAdmin(contextName, getName()); 149 } 150 151 /** 152 * Factory method to create a LoggerConfig. 153 * 154 * @param additivity True if additive, false otherwise. 155 * @param levelName The Level to be associated with the Logger. 156 * @param loggerName The name of the Logger. 157 * @param includeLocation "true" if location should be passed downstream 158 * @param refs An array of Appender names. 159 * @param properties Properties to pass to the Logger. 160 * @param config The Configuration. 161 * @param filter A Filter. 162 * @return A new LoggerConfig. 163 */ 164 @PluginFactory 165 public static LoggerConfig createLogger( 166 @PluginAttribute("additivity") final String additivity, 167 @PluginAttribute("level") final String levelName, 168 @PluginAttribute("name") final String loggerName, 169 @PluginAttribute("includeLocation") final String includeLocation, 170 @PluginElement("AppenderRef") final AppenderRef[] refs, 171 @PluginElement("Properties") final Property[] properties, 172 @PluginConfiguration final Configuration config, 173 @PluginElement("Filters") final Filter filter) { 174 if (loggerName == null) { 175 LOGGER.error("Loggers cannot be configured without a name"); 176 return null; 177 } 178 179 final List<AppenderRef> appenderRefs = Arrays.asList(refs); 180 Level level; 181 try { 182 level = Level.toLevel(levelName, Level.ERROR); 183 } catch (final Exception ex) { 184 LOGGER.error( 185 "Invalid Log level specified: {}. Defaulting to Error", 186 levelName); 187 level = Level.ERROR; 188 } 189 final String name = loggerName.equals("root") ? Strings.EMPTY : loggerName; 190 final boolean additive = Booleans.parseBoolean(additivity, true); 191 192 return new AsyncLoggerConfig(name, appenderRefs, filter, level, 193 additive, properties, config, includeLocation(includeLocation)); 194 } 195 196 // Note: for asynchronous loggers, includeLocation default is FALSE 197 protected static boolean includeLocation(final String includeLocationConfigValue) { 198 return Boolean.parseBoolean(includeLocationConfigValue); 199 } 200 201 /** 202 * An asynchronous root Logger. 203 */ 204 @Plugin(name = "asyncRoot", category = "Core", printObject = true) 205 public static class RootLogger extends LoggerConfig { 206 207 @PluginFactory 208 public static LoggerConfig createLogger( 209 @PluginAttribute("additivity") final String additivity, 210 @PluginAttribute("level") final String levelName, 211 @PluginAttribute("includeLocation") final String includeLocation, 212 @PluginElement("AppenderRef") final AppenderRef[] refs, 213 @PluginElement("Properties") final Property[] properties, 214 @PluginConfiguration final Configuration config, 215 @PluginElement("Filters") final Filter filter) { 216 final List<AppenderRef> appenderRefs = Arrays.asList(refs); 217 Level level; 218 try { 219 level = Level.toLevel(levelName, Level.ERROR); 220 } catch (final Exception ex) { 221 LOGGER.error( 222 "Invalid Log level specified: {}. Defaulting to Error", 223 levelName); 224 level = Level.ERROR; 225 } 226 final boolean additive = Booleans.parseBoolean(additivity, true); 227 228 return new AsyncLoggerConfig(LogManager.ROOT_LOGGER_NAME, 229 appenderRefs, filter, level, additive, properties, config, 230 includeLocation(includeLocation)); 231 } 232 } 233 }