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