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 private final LoggerConfig loggerConfig; 238 private final Configuration config; 239 private final Level level; 240 private final int intLevel; 241 private final Logger logger; 242 243 public PrivateConfig(final Configuration config, final Logger logger) { 244 this.config = config; 245 this.loggerConfig = config.getLoggerConfig(getName()); 246 this.level = this.loggerConfig.getLevel(); 247 this.intLevel = this.level.intLevel(); 248 this.logger = logger; 249 } 250 251 public PrivateConfig(final PrivateConfig pc, final Level level) { 252 this.config = pc.config; 253 this.loggerConfig = pc.loggerConfig; 254 this.level = level; 255 this.intLevel = this.level.intLevel(); 256 this.logger = pc.logger; 257 } 258 259 public PrivateConfig(final PrivateConfig pc, final LoggerConfig lc) { 260 this.config = pc.config; 261 this.loggerConfig = lc; 262 this.level = lc.getLevel(); 263 this.intLevel = this.level.intLevel(); 264 this.logger = pc.logger; 265 } 266 267 protected void logEvent(final LogEvent event) { 268 config.getConfigurationMonitor().checkConfiguration(); 269 loggerConfig.log(event); 270 } 271 272 boolean filter(final Level level, final Marker marker, final String msg) { 273 config.getConfigurationMonitor().checkConfiguration(); 274 final Filter filter = config.getFilter(); 275 if (filter != null) { 276 final Filter.Result r = filter.filter(logger, level, marker, msg); 277 if (r != Filter.Result.NEUTRAL) { 278 return r == Filter.Result.ACCEPT; 279 } 280 } 281 282 return intLevel >= level.intLevel(); 283 } 284 285 boolean filter(final Level level, final Marker marker, final String msg, final Throwable t) { 286 config.getConfigurationMonitor().checkConfiguration(); 287 final Filter filter = config.getFilter(); 288 if (filter != null) { 289 final Filter.Result r = filter.filter(logger, level, marker, msg, t); 290 if (r != Filter.Result.NEUTRAL) { 291 return r == Filter.Result.ACCEPT; 292 } 293 } 294 295 return intLevel >= level.intLevel(); 296 } 297 298 boolean filter(final Level level, final Marker marker, final String msg, final Object... p1) { 299 config.getConfigurationMonitor().checkConfiguration(); 300 final Filter filter = config.getFilter(); 301 if (filter != null) { 302 final Filter.Result r = filter.filter(logger, level, marker, msg, p1); 303 if (r != Filter.Result.NEUTRAL) { 304 return r == Filter.Result.ACCEPT; 305 } 306 } 307 308 return intLevel >= level.intLevel(); 309 } 310 311 boolean filter(final Level level, final Marker marker, final Object msg, final Throwable t) { 312 config.getConfigurationMonitor().checkConfiguration(); 313 final Filter filter = config.getFilter(); 314 if (filter != null) { 315 final Filter.Result r = filter.filter(logger, level, marker, msg, t); 316 if (r != Filter.Result.NEUTRAL) { 317 return r == Filter.Result.ACCEPT; 318 } 319 } 320 321 return intLevel >= level.intLevel(); 322 } 323 324 boolean filter(final Level level, final Marker marker, final Message msg, final Throwable t) { 325 config.getConfigurationMonitor().checkConfiguration(); 326 final Filter filter = config.getFilter(); 327 if (filter != null) { 328 final Filter.Result r = filter.filter(logger, level, marker, msg, t); 329 if (r != Filter.Result.NEUTRAL) { 330 return r == Filter.Result.ACCEPT; 331 } 332 } 333 334 return intLevel >= level.intLevel(); 335 } 336 } 337 338 /** 339 * Returns a String representation of this instance in the form {@code "name:level[ in context_name]"}. 340 * @return A String describing this Logger instance. 341 */ 342 @Override 343 public String toString() { 344 final String nameLevel = "" + getName() + ":" + getLevel(); 345 if (context == null) { 346 return nameLevel; 347 } 348 final String contextName = context.getName(); 349 return contextName == null ? nameLevel : nameLevel + " in " + contextName; 350 } 351 }