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