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