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.util.ArrayList; 020 import java.util.Iterator; 021 import java.util.List; 022 import java.util.Map; 023 024 import org.apache.logging.log4j.Level; 025 import org.apache.logging.log4j.Marker; 026 import org.apache.logging.log4j.core.config.Configuration; 027 import org.apache.logging.log4j.core.config.LoggerConfig; 028 import org.apache.logging.log4j.core.filter.CompositeFilter; 029 import org.apache.logging.log4j.message.Message; 030 import org.apache.logging.log4j.message.MessageFactory; 031 import org.apache.logging.log4j.message.SimpleMessage; 032 import org.apache.logging.log4j.spi.AbstractLogger; 033 import org.apache.logging.log4j.util.Strings; 034 035 /** 036 * The core implementation of the {@link org.apache.logging.log4j.Logger} interface. Besides providing an 037 * implementation of all the Logger methods, this class also provides some convenience methods for Log4j 1.x 038 * compatibility as well as access to the {@link org.apache.logging.log4j.core.Filter Filters} and 039 * {@link org.apache.logging.log4j.core.Appender Appenders} associated with this Logger. Note that access to these 040 * underlying objects is provided primarily for use in unit tests or bridging legacy Log4j 1.x code. Future versions 041 * of this class may or may not include the various methods that are noted as not being part of the public API. 042 * 043 * @doubt All the isEnabled methods could be pushed into a filter interface. Not sure of the utility of having 044 * isEnabled be able to examine the message pattern and parameters. (RG) Moving the isEnabled methods out of 045 * Logger noticeably impacts performance. The message pattern and parameters are required so that they can be 046 * used in global filters. 047 */ 048 public class Logger extends AbstractLogger { 049 050 private static final long serialVersionUID = 1L; 051 052 /** 053 * config should be consistent across threads. 054 */ 055 // FIXME: non-serializable object in serializable class 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 config The new Configuration. 243 */ 244 void updateConfiguration(final Configuration config) { 245 this.config = new PrivateConfig(config, this); 246 } 247 248 /** 249 * The binding between a Logger and its configuration. 250 */ 251 protected class PrivateConfig { 252 // config fields are public to make them visible to Logger subclasses 253 public final LoggerConfig loggerConfig; 254 public final Configuration config; 255 private final Level level; 256 private final int intLevel; 257 private final Logger logger; 258 259 public PrivateConfig(final Configuration config, final Logger logger) { 260 this.config = config; 261 this.loggerConfig = config.getLoggerConfig(getName()); 262 this.level = this.loggerConfig.getLevel(); 263 this.intLevel = this.level.intLevel(); 264 this.logger = logger; 265 } 266 267 public PrivateConfig(final PrivateConfig pc, final Level level) { 268 this.config = pc.config; 269 this.loggerConfig = pc.loggerConfig; 270 this.level = level; 271 this.intLevel = this.level.intLevel(); 272 this.logger = pc.logger; 273 } 274 275 public PrivateConfig(final PrivateConfig pc, final LoggerConfig lc) { 276 this.config = pc.config; 277 this.loggerConfig = lc; 278 this.level = lc.getLevel(); 279 this.intLevel = this.level.intLevel(); 280 this.logger = pc.logger; 281 } 282 283 // LOG4J2-151: changed visibility to public 284 public void logEvent(final LogEvent event) { 285 config.getConfigurationMonitor().checkConfiguration(); 286 loggerConfig.log(event); 287 } 288 289 boolean filter(final Level level, final Marker marker, final String msg) { 290 config.getConfigurationMonitor().checkConfiguration(); 291 final Filter filter = config.getFilter(); 292 if (filter != null) { 293 final Filter.Result r = filter.filter(logger, level, marker, msg); 294 if (r != Filter.Result.NEUTRAL) { 295 return r == Filter.Result.ACCEPT; 296 } 297 } 298 299 return intLevel >= level.intLevel(); 300 } 301 302 boolean filter(final Level level, final Marker marker, final String msg, final Throwable t) { 303 config.getConfigurationMonitor().checkConfiguration(); 304 final Filter filter = config.getFilter(); 305 if (filter != null) { 306 final Filter.Result r = filter.filter(logger, level, marker, msg, t); 307 if (r != Filter.Result.NEUTRAL) { 308 return r == Filter.Result.ACCEPT; 309 } 310 } 311 312 return 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 325 return intLevel >= level.intLevel(); 326 } 327 328 boolean filter(final Level level, final Marker marker, final Object msg, final Throwable t) { 329 config.getConfigurationMonitor().checkConfiguration(); 330 final Filter filter = config.getFilter(); 331 if (filter != null) { 332 final Filter.Result r = filter.filter(logger, level, marker, msg, t); 333 if (r != Filter.Result.NEUTRAL) { 334 return r == Filter.Result.ACCEPT; 335 } 336 } 337 338 return intLevel >= level.intLevel(); 339 } 340 341 boolean filter(final Level level, final Marker marker, final Message msg, final Throwable t) { 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, t); 346 if (r != Filter.Result.NEUTRAL) { 347 return r == Filter.Result.ACCEPT; 348 } 349 } 350 351 return intLevel >= level.intLevel(); 352 } 353 } 354 355 /** 356 * Returns a String representation of this instance in the form {@code "name:level[ in context_name]"}. 357 * @return A String describing this Logger instance. 358 */ 359 @Override 360 public String toString() { 361 final String nameLevel = Strings.EMPTY + getName() + ':' + getLevel(); 362 if (context == null) { 363 return nameLevel; 364 } 365 final String contextName = context.getName(); 366 return contextName == null ? nameLevel : nameLevel + " in " + contextName; 367 } 368 }