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