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