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.filter.CompositeFilter; 030import org.apache.logging.log4j.message.Message; 031import org.apache.logging.log4j.message.MessageFactory; 032import org.apache.logging.log4j.message.SimpleMessage; 033import org.apache.logging.log4j.spi.AbstractLogger; 034import 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 */ 049public 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}