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 }