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