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