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;
018
019 import java.io.Serializable;
020 import java.util.ArrayList;
021 import java.util.Iterator;
022 import java.util.List;
023 import java.util.Map;
024
025 import org.apache.logging.log4j.Level;
026 import org.apache.logging.log4j.Marker;
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.filter.CompositeFilter;
030 import org.apache.logging.log4j.message.Message;
031 import org.apache.logging.log4j.message.MessageFactory;
032 import org.apache.logging.log4j.message.SimpleMessage;
033 import org.apache.logging.log4j.spi.AbstractLogger;
034 import org.apache.logging.log4j.util.Strings;
035
036 /**
037 * The core implementation of the {@link org.apache.logging.log4j.Logger} interface. Besides providing an
038 * implementation of all the Logger methods, this class also provides some convenience methods for Log4j 1.x
039 * compatibility as well as access to the {@link org.apache.logging.log4j.core.Filter Filters} and
040 * {@link org.apache.logging.log4j.core.Appender Appenders} associated with this Logger. Note that access to these
041 * underlying objects is provided primarily for use in unit tests or bridging legacy Log4j 1.x code. Future versions
042 * of this class may or may not include the various methods that are noted as not being part of the public API.
043 *
044 * TODO All the isEnabled methods could be pushed into a filter interface. Not sure of the utility of having
045 * isEnabled be able to examine the message pattern and parameters. (RG) Moving the isEnabled methods out of
046 * Logger noticeably impacts performance. The message pattern and parameters are required so that they can be
047 * used in global filters.
048 */
049 public class Logger extends AbstractLogger {
050
051 private static final long serialVersionUID = 1L;
052
053 /**
054 * Config should be consistent across threads.
055 */
056 protected volatile PrivateConfig config;
057
058 // FIXME: ditto to the above
059 private final LoggerContext context;
060
061 /**
062 * The constructor.
063 * @param context The LoggerContext this Logger is associated with.
064 * @param messageFactory The message factory.
065 * @param name The name of the Logger.
066 */
067 protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
068 super(name, messageFactory);
069 this.context = context;
070 config = new PrivateConfig(context.getConfiguration(), this);
071 }
072
073 /**
074 * This method is only used for 1.x compatibility.
075 * Returns the parent of this Logger. If it doesn't already exist return a temporary Logger.
076 * @return The parent Logger.
077 */
078 public Logger getParent() {
079 final LoggerConfig lc = config.loggerConfig.getName().equals(getName()) ? config.loggerConfig.getParent() :
080 config.loggerConfig;
081 if (lc == null) {
082 return null;
083 }
084 if (context.hasLogger(lc.getName())) {
085 return context.getLogger(lc.getName(), getMessageFactory());
086 }
087 return new Logger(context, lc.getName(), this.getMessageFactory());
088 }
089
090 /**
091 * Returns the LoggerContext this Logger is associated with.
092 * @return the LoggerContext.
093 */
094 public LoggerContext getContext() {
095 return context;
096 }
097
098 /**
099 * This method is not exposed through the public API and is provided primarily for unit testing.
100 * @param level The Level to use on this Logger.
101 */
102 public synchronized void setLevel(final Level level) {
103 if (level != null) {
104 config = new PrivateConfig(config, level);
105 }
106 }
107
108 @Override
109 public void logMessage(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable t) {
110 final Message msg = message == null ? new SimpleMessage(Strings.EMPTY) : message;
111 config.config.getConfigurationMonitor().checkConfiguration();
112 config.loggerConfig.log(getName(), fqcn, marker, level, msg, t);
113 }
114
115 @Override
116 public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) {
117 return config.filter(level, marker, message, t);
118 }
119
120 @Override
121 public boolean isEnabled(final Level level, final Marker marker, final String message) {
122 return config.filter(level, marker, message);
123 }
124
125 @Override
126 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object... params) {
127 return config.filter(level, marker, message, params);
128 }
129
130 @Override
131 public boolean isEnabled(final Level level, final Marker marker, final Object message, final Throwable t) {
132 return config.filter(level, marker, message, t);
133 }
134
135 @Override
136 public boolean isEnabled(final Level level, final Marker marker, final Message message, final Throwable t) {
137 return config.filter(level, marker, message, t);
138 }
139
140 /**
141 * This method is not exposed through the public API and is used primarily for unit testing.
142 * @param appender The Appender to add to the Logger.
143 */
144 public void addAppender(final Appender appender) {
145 config.config.addLoggerAppender(this, appender);
146 }
147
148 /**
149 * This method is not exposed through the public API and is used primarily for unit testing.
150 * @param appender The Appender to remove from the Logger.
151 */
152 public void removeAppender(final Appender appender) {
153 config.loggerConfig.removeAppender(appender.getName());
154 }
155
156 /**
157 * This method is not exposed through the public API and is used primarily for unit testing.
158 * @return A Map containing the Appender's name as the key and the Appender as the value.
159 */
160 public Map<String, Appender> getAppenders() {
161 return config.loggerConfig.getAppenders();
162 }
163
164 /**
165 * This method is not exposed through the public API and is used primarily for unit testing.
166 * @return An Iterator over all the Filters associated with the Logger.
167 */
168 // FIXME: this really ought to be an Iterable instead of an Iterator
169 public Iterator<Filter> getFilters() {
170 final Filter filter = config.loggerConfig.getFilter();
171 if (filter == null) {
172 return new ArrayList<Filter>().iterator();
173 } else if (filter instanceof CompositeFilter) {
174 return ((CompositeFilter) filter).iterator();
175 } else {
176 final List<Filter> filters = new ArrayList<Filter>();
177 filters.add(filter);
178 return filters.iterator();
179 }
180 }
181
182 /**
183 * Gets the Level associated with the Logger.
184 *
185 * @return the Level associate with the Logger.
186 */
187 @Override
188 public Level getLevel() {
189 return config.level;
190 }
191
192 /**
193 * This method is not exposed through the public API and is used primarily for unit testing.
194 * @return The number of Filters associated with the Logger.
195 */
196 public int filterCount() {
197 final Filter filter = config.loggerConfig.getFilter();
198 if (filter == null) {
199 return 0;
200 } else if (filter instanceof CompositeFilter) {
201 return ((CompositeFilter) filter).size();
202 }
203 return 1;
204 }
205
206 /**
207 * This method is not exposed through the public API and is used primarily for unit testing.
208 * @param filter The Filter to add.
209 */
210 public void addFilter(final Filter filter) {
211 config.config.addLoggerFilter(this, filter);
212 }
213
214 /**
215 * This method is not exposed through the public API and is present only to support the Log4j 1.2
216 * compatibility bridge.
217 * @return true if the associated LoggerConfig is additive, false otherwise.
218 */
219 public boolean isAdditive() {
220 return config.loggerConfig.isAdditive();
221 }
222
223 /**
224 * This method is not exposed through the public API and is present only to support the Log4j 1.2
225 * compatibility bridge.
226 * @param additive Boolean value to indicate whether the Logger is additive or not.
227 */
228 public void setAdditive(final boolean additive) {
229 config.config.setLoggerAdditive(this, additive);
230 }
231
232 /**
233 * Associates the Logger with a new Configuration. This method is not exposed through the
234 * public API.
235 *
236 * There are two ways that could be used to guarantee all threads are aware of changes to
237 * config. 1. synchronize this method. Accessors don't need to be synchronized as Java will
238 * treat all variables within a synchronized block as volatile. 2. Declare the variable
239 * volatile. Option 2 is used here as the performance cost is very low and it does a better
240 * job at documenting how it is used.
241 *
242 * @param newConfig The new Configuration.
243 */
244 protected void updateConfiguration(final Configuration newConfig) {
245 this.config = new PrivateConfig(newConfig, this);
246 }
247
248 /**
249 * The binding between a Logger and its configuration.
250 */
251 // TODO: Should not be Serializable per EJ item 74 (2nd Ed)?
252 protected class PrivateConfig implements Serializable {
253 private static final long serialVersionUID = 1L;
254 // config fields are public to make them visible to Logger subclasses
255 public final LoggerConfig loggerConfig;
256 public final Configuration config;
257 private final Level level;
258 private final int intLevel;
259 private final Logger logger;
260
261 public PrivateConfig(final Configuration config, final Logger logger) {
262 this.config = config;
263 this.loggerConfig = config.getLoggerConfig(getName());
264 this.level = this.loggerConfig.getLevel();
265 this.intLevel = this.level.intLevel();
266 this.logger = logger;
267 }
268
269 public PrivateConfig(final PrivateConfig pc, final Level level) {
270 this.config = pc.config;
271 this.loggerConfig = pc.loggerConfig;
272 this.level = level;
273 this.intLevel = this.level.intLevel();
274 this.logger = pc.logger;
275 }
276
277 public PrivateConfig(final PrivateConfig pc, final LoggerConfig lc) {
278 this.config = pc.config;
279 this.loggerConfig = lc;
280 this.level = lc.getLevel();
281 this.intLevel = this.level.intLevel();
282 this.logger = pc.logger;
283 }
284
285 // LOG4J2-151: changed visibility to public
286 public void logEvent(final LogEvent event) {
287 config.getConfigurationMonitor().checkConfiguration();
288 loggerConfig.log(event);
289 }
290
291 boolean filter(final Level level, final Marker marker, final String msg) {
292 config.getConfigurationMonitor().checkConfiguration();
293 final Filter filter = config.getFilter();
294 if (filter != null) {
295 final Filter.Result r = filter.filter(logger, level, marker, msg);
296 if (r != Filter.Result.NEUTRAL) {
297 return r == Filter.Result.ACCEPT;
298 }
299 }
300 return level != null && intLevel >= level.intLevel();
301 }
302
303 boolean filter(final Level level, final Marker marker, final String msg, final Throwable t) {
304 config.getConfigurationMonitor().checkConfiguration();
305 final Filter filter = config.getFilter();
306 if (filter != null) {
307 final Filter.Result r = filter.filter(logger, level, marker, msg, t);
308 if (r != Filter.Result.NEUTRAL) {
309 return r == Filter.Result.ACCEPT;
310 }
311 }
312 return level != null && intLevel >= level.intLevel();
313 }
314
315 boolean filter(final Level level, final Marker marker, final String msg, final Object... p1) {
316 config.getConfigurationMonitor().checkConfiguration();
317 final Filter filter = config.getFilter();
318 if (filter != null) {
319 final Filter.Result r = filter.filter(logger, level, marker, msg, p1);
320 if (r != Filter.Result.NEUTRAL) {
321 return r == Filter.Result.ACCEPT;
322 }
323 }
324 return level != null && intLevel >= level.intLevel();
325 }
326
327 boolean filter(final Level level, final Marker marker, final Object msg, final Throwable t) {
328 config.getConfigurationMonitor().checkConfiguration();
329 final Filter filter = config.getFilter();
330 if (filter != null) {
331 final Filter.Result r = filter.filter(logger, level, marker, msg, t);
332 if (r != Filter.Result.NEUTRAL) {
333 return r == Filter.Result.ACCEPT;
334 }
335 }
336 return level != null && intLevel >= level.intLevel();
337 }
338
339 boolean filter(final Level level, final Marker marker, final Message msg, final Throwable t) {
340 config.getConfigurationMonitor().checkConfiguration();
341 final Filter filter = config.getFilter();
342 if (filter != null) {
343 final Filter.Result r = filter.filter(logger, level, marker, msg, t);
344 if (r != Filter.Result.NEUTRAL) {
345 return r == Filter.Result.ACCEPT;
346 }
347 }
348 return level != null && intLevel >= level.intLevel();
349 }
350 }
351
352 /**
353 * Returns a String representation of this instance in the form {@code "name:level[ in context_name]"}.
354 * @return A String describing this Logger instance.
355 */
356 @Override
357 public String toString() {
358 final String nameLevel = Strings.EMPTY + getName() + ':' + getLevel();
359 if (context == null) {
360 return nameLevel;
361 }
362 final String contextName = context.getName();
363 return contextName == null ? nameLevel : nameLevel + " in " + contextName;
364 }
365 }