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.Logger; 025 import org.apache.logging.log4j.core.Filter; 026 import org.apache.logging.log4j.core.LogEvent; 027 import org.apache.logging.log4j.core.LoggerContext; 028 import org.apache.logging.log4j.core.LoggerContext.Status; 029 import org.apache.logging.log4j.core.config.AppenderRef; 030 import org.apache.logging.log4j.core.config.Configuration; 031 import org.apache.logging.log4j.core.config.LoggerConfig; 032 import org.apache.logging.log4j.core.config.Property; 033 import org.apache.logging.log4j.core.config.plugins.Plugin; 034 import org.apache.logging.log4j.core.config.plugins.PluginAttr; 035 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 036 import org.apache.logging.log4j.core.config.plugins.PluginElement; 037 import org.apache.logging.log4j.core.config.plugins.PluginFactory; 038 import org.apache.logging.log4j.status.StatusLogger; 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 FastFileAppender or 062 * FastRollingFileAppender, 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", type = "Core", printObject = true) 069 public class AsyncLoggerConfig extends LoggerConfig { 070 071 private static final Logger LOGGER = StatusLogger.getLogger(); 072 private AsyncLoggerConfigHelper helper; 073 074 /** 075 * Default constructor. 076 */ 077 public AsyncLoggerConfig() { 078 super(); 079 } 080 081 /** 082 * Constructor that sets the name, level and additive values. 083 * 084 * @param name The Logger name. 085 * @param level The Level. 086 * @param additive true if the Logger is additive, false otherwise. 087 */ 088 public AsyncLoggerConfig(final String name, final Level level, 089 final boolean additive) { 090 super(name, level, additive); 091 } 092 093 protected AsyncLoggerConfig(final String name, 094 final List<AppenderRef> appenders, final Filter filter, 095 final Level level, final boolean additive, 096 final Property[] properties, final Configuration config, 097 final boolean includeLocation) { 098 super(name, appenders, filter, level, additive, properties, config, 099 includeLocation); 100 } 101 102 /** 103 * Passes on the event to a separate thread that will call 104 * {@link #asyncCallAppenders(LogEvent)}. 105 */ 106 @Override 107 protected void callAppenders(LogEvent event) { 108 // populate lazily initialized fields 109 event.getSource(); 110 event.getThreadName(); 111 112 // pass on the event to a separate thread 113 helper.callAppendersFromAnotherThread(event); 114 } 115 116 /** Called by AsyncLoggerConfigHelper.RingBufferLog4jEventHandler. */ 117 void asyncCallAppenders(LogEvent event) { 118 super.callAppenders(event); 119 } 120 121 @Override 122 public void startFilter() { 123 if (helper == null) { 124 helper = new AsyncLoggerConfigHelper(this); 125 } 126 super.startFilter(); 127 } 128 129 @Override 130 public void stopFilter() { 131 // only stop disruptor if shutting down logging subsystem 132 if (LogManager.getContext() instanceof LoggerContext) { 133 if (((LoggerContext) LogManager.getContext()).getStatus() != Status.STOPPING) { 134 return; 135 } 136 } 137 helper.shutdown(); 138 super.stopFilter(); 139 } 140 141 /** 142 * Factory method to create a LoggerConfig. 143 * 144 * @param additivity True if additive, false otherwise. 145 * @param levelName The Level to be associated with the Logger. 146 * @param loggerName The name of the Logger. 147 * @param includeLocation "true" if location should be passed downstream 148 * @param refs An array of Appender names. 149 * @param properties Properties to pass to the Logger. 150 * @param config The Configuration. 151 * @param filter A Filter. 152 * @return A new LoggerConfig. 153 */ 154 @PluginFactory 155 public static LoggerConfig createLogger( 156 @PluginAttr("additivity") final String additivity, 157 @PluginAttr("level") final String levelName, 158 @PluginAttr("name") final String loggerName, 159 @PluginAttr("includeLocation") final String includeLocation, 160 @PluginElement("appender-ref") final AppenderRef[] refs, 161 @PluginElement("properties") final Property[] properties, 162 @PluginConfiguration final Configuration config, 163 @PluginElement("filters") final Filter filter) { 164 if (loggerName == null) { 165 LOGGER.error("Loggers cannot be configured without a name"); 166 return null; 167 } 168 169 final List<AppenderRef> appenderRefs = Arrays.asList(refs); 170 Level level; 171 try { 172 level = Level.toLevel(levelName, Level.ERROR); 173 } catch (final Exception ex) { 174 LOGGER.error( 175 "Invalid Log level specified: {}. Defaulting to Error", 176 levelName); 177 level = Level.ERROR; 178 } 179 final String name = loggerName.equals("root") ? "" : loggerName; 180 final boolean additive = additivity == null ? true : Boolean 181 .parseBoolean(additivity); 182 183 return new AsyncLoggerConfig(name, appenderRefs, filter, level, 184 additive, properties, config, includeLocation(includeLocation)); 185 } 186 187 // Note: for asynchronous loggers, includeLocation default is FALSE 188 private static boolean includeLocation(String includeLocationConfigValue) { 189 if (includeLocationConfigValue == null) { 190 return false; 191 } 192 return Boolean.parseBoolean(includeLocationConfigValue); 193 } 194 195 /** 196 * An asynchronous root Logger. 197 */ 198 @Plugin(name = "asyncRoot", type = "Core", printObject = true) 199 public static class RootLogger extends LoggerConfig { 200 201 @PluginFactory 202 public static LoggerConfig createLogger( 203 @PluginAttr("additivity") final String additivity, 204 @PluginAttr("level") final String levelName, 205 @PluginAttr("includeLocation") final String includeLocation, 206 @PluginElement("appender-ref") final AppenderRef[] refs, 207 @PluginElement("properties") final Property[] properties, 208 @PluginConfiguration final Configuration config, 209 @PluginElement("filters") final Filter filter) { 210 final List<AppenderRef> appenderRefs = Arrays.asList(refs); 211 Level level; 212 try { 213 level = Level.toLevel(levelName, Level.ERROR); 214 } catch (final Exception ex) { 215 LOGGER.error( 216 "Invalid Log level specified: {}. Defaulting to Error", 217 levelName); 218 level = Level.ERROR; 219 } 220 final boolean additive = additivity == null ? true : Boolean 221 .parseBoolean(additivity); 222 223 return new AsyncLoggerConfig(LogManager.ROOT_LOGGER_NAME, 224 appenderRefs, filter, level, additive, properties, config, 225 includeLocation(includeLocation)); 226 } 227 } 228 }