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