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 final AsyncLoggerConfigDelegate delegate; 072 073 protected AsyncLoggerConfig(final String name, 074 final List<AppenderRef> appenders, final Filter filter, 075 final Level level, final boolean additive, 076 final Property[] properties, final Configuration config, 077 final boolean includeLocation) { 078 super(name, appenders, filter, level, additive, properties, config, 079 includeLocation); 080 delegate = config.getAsyncLoggerConfigDelegate(); 081 delegate.setLogEventFactory(getLogEventFactory()); 082 } 083 084 /** 085 * Passes on the event to a separate thread that will call 086 * {@link #asyncCallAppenders(LogEvent)}. 087 */ 088 @Override 089 protected void callAppenders(final LogEvent event) { 090 populateLazilyInitializedFields(event); 091 092 if (!delegate.tryEnqueue(event, this)) { 093 final EventRoute eventRoute = delegate.getEventRoute(event.getLevel()); 094 eventRoute.logMessage(this, event); 095 } 096 } 097 098 private void populateLazilyInitializedFields(final LogEvent event) { 099 event.getSource(); 100 event.getThreadName(); 101 } 102 103 void callAppendersInCurrentThread(final LogEvent event) { 104 super.callAppenders(event); 105 } 106 107 void callAppendersInBackgroundThread(final LogEvent event) { 108 delegate.enqueueEvent(event, this); 109 } 110 111 /** Called by AsyncLoggerConfigHelper.RingBufferLog4jEventHandler. */ 112 void asyncCallAppenders(final LogEvent event) { 113 super.callAppenders(event); 114 } 115 116 private String displayName() { 117 return LogManager.ROOT_LOGGER_NAME.equals(getName()) ? LoggerConfig.ROOT : getName(); 118 } 119 120 @Override 121 public void start() { 122 LOGGER.trace("AsyncLoggerConfig[{}] starting...", displayName()); 123 super.start(); 124 } 125 126 @Override 127 public void stop() { 128 LOGGER.trace("AsyncLoggerConfig[{}] stopping...", displayName()); 129 super.stop(); 130 } 131 132 /** 133 * Creates and returns a new {@code RingBufferAdmin} that instruments the 134 * ringbuffer of this {@code AsyncLoggerConfig}. 135 * 136 * @param contextName name of the {@code LoggerContext} 137 * @return a new {@code RingBufferAdmin} that instruments the ringbuffer 138 */ 139 public RingBufferAdmin createRingBufferAdmin(final String contextName) { 140 return delegate.createRingBufferAdmin(contextName, getName()); 141 } 142 143 /** 144 * Factory method to create a LoggerConfig. 145 * 146 * @param additivity True if additive, false otherwise. 147 * @param levelName The Level to be associated with the Logger. 148 * @param loggerName The name of the Logger. 149 * @param includeLocation "true" if location should be passed downstream 150 * @param refs An array of Appender names. 151 * @param properties Properties to pass to the Logger. 152 * @param config The Configuration. 153 * @param filter A Filter. 154 * @return A new LoggerConfig. 155 */ 156 @PluginFactory 157 public static LoggerConfig createLogger( 158 @PluginAttribute("additivity") final String additivity, 159 @PluginAttribute("level") final String levelName, 160 @PluginAttribute("name") final String loggerName, 161 @PluginAttribute("includeLocation") final String includeLocation, 162 @PluginElement("AppenderRef") final AppenderRef[] refs, 163 @PluginElement("Properties") final Property[] properties, 164 @PluginConfiguration final Configuration config, 165 @PluginElement("Filter") final Filter filter) { 166 if (loggerName == null) { 167 LOGGER.error("Loggers cannot be configured without a name"); 168 return null; 169 } 170 171 final List<AppenderRef> appenderRefs = Arrays.asList(refs); 172 Level level; 173 try { 174 level = Level.toLevel(levelName, Level.ERROR); 175 } catch (final Exception ex) { 176 LOGGER.error( 177 "Invalid Log level specified: {}. Defaulting to Error", 178 levelName); 179 level = Level.ERROR; 180 } 181 final String name = loggerName.equals(LoggerConfig.ROOT) ? Strings.EMPTY : loggerName; 182 final boolean additive = Booleans.parseBoolean(additivity, true); 183 184 return new AsyncLoggerConfig(name, appenderRefs, filter, level, 185 additive, properties, config, includeLocation(includeLocation)); 186 } 187 188 // Note: for asynchronous loggers, includeLocation default is FALSE 189 protected static boolean includeLocation(final String includeLocationConfigValue) { 190 return Boolean.parseBoolean(includeLocationConfigValue); 191 } 192 193 /** 194 * An asynchronous root Logger. 195 */ 196 @Plugin(name = "asyncRoot", category = "Core", printObject = true) 197 public static class RootLogger extends LoggerConfig { 198 199 @PluginFactory 200 public static LoggerConfig createLogger( 201 @PluginAttribute("additivity") final String additivity, 202 @PluginAttribute("level") final String levelName, 203 @PluginAttribute("includeLocation") final String includeLocation, 204 @PluginElement("AppenderRef") final AppenderRef[] refs, 205 @PluginElement("Properties") final Property[] properties, 206 @PluginConfiguration final Configuration config, 207 @PluginElement("Filter") final Filter filter) { 208 final List<AppenderRef> appenderRefs = Arrays.asList(refs); 209 Level level; 210 try { 211 level = Level.toLevel(levelName, Level.ERROR); 212 } catch (final Exception ex) { 213 LOGGER.error( 214 "Invalid Log level specified: {}. Defaulting to Error", 215 levelName); 216 level = Level.ERROR; 217 } 218 final boolean additive = Booleans.parseBoolean(additivity, true); 219 220 return new AsyncLoggerConfig(LogManager.ROOT_LOGGER_NAME, 221 appenderRefs, filter, level, additive, properties, config, 222 AsyncLoggerConfig.includeLocation(includeLocation)); 223 } 224 } 225}