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