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.io.ObjectStreamException;
020import java.io.Serializable;
021import java.util.ArrayList;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Map;
025
026import org.apache.logging.log4j.Level;
027import org.apache.logging.log4j.Marker;
028import org.apache.logging.log4j.core.config.Configuration;
029import org.apache.logging.log4j.core.config.LoggerConfig;
030import org.apache.logging.log4j.core.config.ReliabilityStrategy;
031import org.apache.logging.log4j.core.filter.CompositeFilter;
032import org.apache.logging.log4j.message.Message;
033import org.apache.logging.log4j.message.MessageFactory;
034import org.apache.logging.log4j.message.SimpleMessage;
035import org.apache.logging.log4j.spi.AbstractLogger;
036import org.apache.logging.log4j.util.Strings;
037import org.apache.logging.log4j.util.Supplier;
038
039/**
040 * The core implementation of the {@link org.apache.logging.log4j.Logger} interface. Besides providing an implementation
041 * of all the Logger methods, this class also provides some convenience methods for Log4j 1.x compatibility as well as
042 * access to the {@link org.apache.logging.log4j.core.Filter Filters} and {@link org.apache.logging.log4j.core.Appender
043 * Appenders} associated with this Logger. Note that access to these underlying objects is provided primarily for use in
044 * unit tests or bridging legacy Log4j 1.x code. Future versions of this class may or may not include the various
045 * methods that are noted as not being part of the public API.
046 *
047 * TODO All the isEnabled methods could be pushed into a filter interface. Not sure of the utility of having isEnabled
048 * be able to examine the message pattern and parameters. (RG) Moving the isEnabled methods out of Logger noticeably
049 * impacts performance. The message pattern and parameters are required so that they can be used in global filters.
050 */
051public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
052
053    private static final long serialVersionUID = 1L;
054
055    /**
056     * Config should be consistent across threads.
057     */
058    protected volatile PrivateConfig privateConfig;
059
060    // FIXME: ditto to the above
061    private final LoggerContext context;
062
063    /**
064     * The constructor.
065     *
066     * @param context The LoggerContext this Logger is associated with.
067     * @param messageFactory The message factory.
068     * @param name The name of the Logger.
069     */
070    protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
071        super(name, messageFactory);
072        this.context = context;
073        privateConfig = new PrivateConfig(context.getConfiguration(), this);
074    }
075
076    protected Object writeReplace() throws ObjectStreamException {
077        return new LoggerProxy(getName(), getMessageFactory());
078    }
079
080    /**
081     * This method is only used for 1.x compatibility. Returns the parent of this Logger. If it doesn't already exist
082     * return a temporary Logger.
083     *
084     * @return The parent Logger.
085     */
086    public Logger getParent() {
087        final LoggerConfig lc = privateConfig.loggerConfig.getName().equals(getName()) ? privateConfig.loggerConfig
088                .getParent() : privateConfig.loggerConfig;
089        if (lc == null) {
090            return null;
091        }
092        final String lcName = lc.getName();
093        final MessageFactory messageFactory = getMessageFactory();
094        if (context.hasLogger(lcName, messageFactory)) {
095            return context.getLogger(lcName, messageFactory);
096        }
097        return new Logger(context, lcName, messageFactory);
098    }
099
100    /**
101     * Returns the LoggerContext this Logger is associated with.
102     *
103     * @return the LoggerContext.
104     */
105    public LoggerContext getContext() {
106        return context;
107    }
108
109    /**
110     * This method is not exposed through the public API and is provided primarily for unit testing.
111     * <p>
112     * If the new level is null, this logger inherits the level from its parent.
113     * </p>
114     *
115     * @param level The Level to use on this Logger, may be null.
116     */
117    public synchronized void setLevel(final Level level) {
118        if (level == getLevel()) {
119            return;
120        }
121        Level actualLevel;
122        if (level != null) {
123            actualLevel = level;
124        } else {
125            final Logger parent = getParent();
126            actualLevel = parent != null ? parent.getLevel() : privateConfig.loggerConfigLevel;
127        }
128        privateConfig = new PrivateConfig(privateConfig, actualLevel);
129    }
130
131    /*
132     * (non-Javadoc)
133     *
134     * @see org.apache.logging.log4j.util.Supplier#get()
135     */
136    @Override
137    public LoggerConfig get() {
138        return privateConfig.loggerConfig;
139    }
140
141    @Override
142    public void logMessage(final String fqcn, final Level level, final Marker marker, final Message message,
143            final Throwable t) {
144        final Message msg = message == null ? new SimpleMessage(Strings.EMPTY) : message;
145        final ReliabilityStrategy strategy = privateConfig.loggerConfig.getReliabilityStrategy();
146        strategy.log(this, getName(), fqcn, marker, level, msg, t);
147    }
148
149    @Override
150    public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) {
151        return privateConfig.filter(level, marker, message, t);
152    }
153
154    @Override
155    public boolean isEnabled(final Level level, final Marker marker, final String message) {
156        return privateConfig.filter(level, marker, message);
157    }
158
159    @Override
160    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object... params) {
161        return privateConfig.filter(level, marker, message, params);
162    }
163
164    @Override
165    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0) {
166        return privateConfig.filter(level, marker, message, p0);
167    }
168
169    @Override
170    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
171            final Object p1) {
172        return privateConfig.filter(level, marker, message, p0, p1);
173    }
174
175    @Override
176    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
177            final Object p1, final Object p2) {
178        return privateConfig.filter(level, marker, message, p0, p1, p2);
179    }
180
181    @Override
182    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
183            final Object p1, final Object p2, final Object p3) {
184        return privateConfig.filter(level, marker, message, p0, p1, p2, p3);
185    }
186
187    @Override
188    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
189            final Object p1, final Object p2, final Object p3,
190            final Object p4) {
191        return privateConfig.filter(level, marker, message, p0, p1, p2, p3, p4);
192    }
193
194    @Override
195    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
196            final Object p1, final Object p2, final Object p3,
197            final Object p4, final Object p5) {
198        return privateConfig.filter(level, marker, message, p0, p1, p2, p3, p4, p5);
199    }
200
201    @Override
202    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
203            final Object p1, final Object p2, final Object p3,
204            final Object p4, final Object p5, final Object p6) {
205        return privateConfig.filter(level, marker, message, p0, p1, p2, p3, p4, p5, p6);
206    }
207
208    @Override
209    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
210            final Object p1, final Object p2, final Object p3,
211            final Object p4, final Object p5, final Object p6,
212            final Object p7) {
213        return privateConfig.filter(level, marker, message, p0, p1, p2, p3, p4, p5, p6, p7);
214    }
215
216    @Override
217    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
218            final Object p1, final Object p2, final Object p3,
219            final Object p4, final Object p5, final Object p6,
220            final Object p7, final Object p8) {
221        return privateConfig.filter(level, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8);
222    }
223
224    @Override
225    public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
226            final Object p1, final Object p2, final Object p3,
227            final Object p4, final Object p5, final Object p6,
228            final Object p7, final Object p8, final Object p9) {
229        return privateConfig.filter(level, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
230    }
231
232    @Override
233    public boolean isEnabled(final Level level, final Marker marker, final CharSequence message, final Throwable t) {
234        return privateConfig.filter(level, marker, message, t);
235    }
236
237    @Override
238    public boolean isEnabled(final Level level, final Marker marker, final Object message, final Throwable t) {
239        return privateConfig.filter(level, marker, message, t);
240    }
241
242    @Override
243    public boolean isEnabled(final Level level, final Marker marker, final Message message, final Throwable t) {
244        return privateConfig.filter(level, marker, message, t);
245    }
246
247    /**
248     * This method is not exposed through the public API and is used primarily for unit testing.
249     *
250     * @param appender The Appender to add to the Logger.
251     */
252    public void addAppender(final Appender appender) {
253        privateConfig.config.addLoggerAppender(this, appender);
254    }
255
256    /**
257     * This method is not exposed through the public API and is used primarily for unit testing.
258     *
259     * @param appender The Appender to remove from the Logger.
260     */
261    public void removeAppender(final Appender appender) {
262        privateConfig.loggerConfig.removeAppender(appender.getName());
263    }
264
265    /**
266     * This method is not exposed through the public API and is used primarily for unit testing.
267     *
268     * @return A Map containing the Appender's name as the key and the Appender as the value.
269     */
270    public Map<String, Appender> getAppenders() {
271        return privateConfig.loggerConfig.getAppenders();
272    }
273
274    /**
275     * This method is not exposed through the public API and is used primarily for unit testing.
276     *
277     * @return An Iterator over all the Filters associated with the Logger.
278     */
279    // FIXME: this really ought to be an Iterable instead of an Iterator
280    public Iterator<Filter> getFilters() {
281        final Filter filter = privateConfig.loggerConfig.getFilter();
282        if (filter == null) {
283            return new ArrayList<Filter>().iterator();
284        } else if (filter instanceof CompositeFilter) {
285            return ((CompositeFilter) filter).iterator();
286        } else {
287            final List<Filter> filters = new ArrayList<>();
288            filters.add(filter);
289            return filters.iterator();
290        }
291    }
292
293    /**
294     * Gets the Level associated with the Logger.
295     *
296     * @return the Level associate with the Logger.
297     */
298    @Override
299    public Level getLevel() {
300        return privateConfig.loggerConfigLevel;
301    }
302
303    /**
304     * This method is not exposed through the public API and is used primarily for unit testing.
305     *
306     * @return The number of Filters associated with the Logger.
307     */
308    public int filterCount() {
309        final Filter filter = privateConfig.loggerConfig.getFilter();
310        if (filter == null) {
311            return 0;
312        } else if (filter instanceof CompositeFilter) {
313            return ((CompositeFilter) filter).size();
314        }
315        return 1;
316    }
317
318    /**
319     * This method is not exposed through the public API and is used primarily for unit testing.
320     *
321     * @param filter The Filter to add.
322     */
323    public void addFilter(final Filter filter) {
324        privateConfig.config.addLoggerFilter(this, filter);
325    }
326
327    /**
328     * This method is not exposed through the public API and is present only to support the Log4j 1.2 compatibility
329     * bridge.
330     *
331     * @return true if the associated LoggerConfig is additive, false otherwise.
332     */
333    public boolean isAdditive() {
334        return privateConfig.loggerConfig.isAdditive();
335    }
336
337    /**
338     * This method is not exposed through the public API and is present only to support the Log4j 1.2 compatibility
339     * bridge.
340     *
341     * @param additive Boolean value to indicate whether the Logger is additive or not.
342     */
343    public void setAdditive(final boolean additive) {
344        privateConfig.config.setLoggerAdditive(this, additive);
345    }
346
347    /**
348     * Associates this Logger with a new Configuration. This method is not
349     * exposed through the public API.
350     * <p>
351     * There are two ways this could be used to guarantee all threads are aware
352     * of changes to config.
353     * <ol>
354     * <li>Synchronize this method. Accessors don't need to be synchronized as
355     * Java will treat all variables within a synchronized block as volatile.
356     * </li>
357     * <li>Declare the variable volatile. Option 2 is used here as the
358     * performance cost is very low and it does a better job at documenting how
359     * it is used.</li>
360     *
361     * @param newConfig
362     *            The new Configuration.
363     */
364    protected void updateConfiguration(final Configuration newConfig) {
365        this.privateConfig = new PrivateConfig(newConfig, this);
366    }
367
368    /**
369     * The binding between a Logger and its configuration.
370     */
371    protected class PrivateConfig {
372        // config fields are public to make them visible to Logger subclasses
373        /** LoggerConfig to delegate the actual logging to. */
374        public final LoggerConfig loggerConfig; // SUPPRESS CHECKSTYLE
375        /** The current Configuration associated with the LoggerConfig. */
376        public final Configuration config; // SUPPRESS CHECKSTYLE
377        private final Level loggerConfigLevel;
378        private final int intLevel;
379        private final Logger logger;
380
381        public PrivateConfig(final Configuration config, final Logger logger) {
382            this.config = config;
383            this.loggerConfig = config.getLoggerConfig(getName());
384            this.loggerConfigLevel = this.loggerConfig.getLevel();
385            this.intLevel = this.loggerConfigLevel.intLevel();
386            this.logger = logger;
387        }
388
389        public PrivateConfig(final PrivateConfig pc, final Level level) {
390            this.config = pc.config;
391            this.loggerConfig = pc.loggerConfig;
392            this.loggerConfigLevel = level;
393            this.intLevel = this.loggerConfigLevel.intLevel();
394            this.logger = pc.logger;
395        }
396
397        public PrivateConfig(final PrivateConfig pc, final LoggerConfig lc) {
398            this.config = pc.config;
399            this.loggerConfig = lc;
400            this.loggerConfigLevel = lc.getLevel();
401            this.intLevel = this.loggerConfigLevel.intLevel();
402            this.logger = pc.logger;
403        }
404
405        // LOG4J2-151: changed visibility to public
406        public void logEvent(final LogEvent event) {
407            loggerConfig.log(event);
408        }
409
410        boolean filter(final Level level, final Marker marker, final String msg) {
411            final Filter filter = config.getFilter();
412            if (filter != null) {
413                final Filter.Result r = filter.filter(logger, level, marker, msg);
414                if (r != Filter.Result.NEUTRAL) {
415                    return r == Filter.Result.ACCEPT;
416                }
417            }
418            return level != null && intLevel >= level.intLevel();
419        }
420
421        boolean filter(final Level level, final Marker marker, final String msg, final Throwable t) {
422            final Filter filter = config.getFilter();
423            if (filter != null) {
424                final Filter.Result r = filter.filter(logger, level, marker, (Object) msg, t);
425                if (r != Filter.Result.NEUTRAL) {
426                    return r == Filter.Result.ACCEPT;
427                }
428            }
429            return level != null && intLevel >= level.intLevel();
430        }
431
432        boolean filter(final Level level, final Marker marker, final String msg, final Object... p1) {
433            final Filter filter = config.getFilter();
434            if (filter != null) {
435                final Filter.Result r = filter.filter(logger, level, marker, msg, p1);
436                if (r != Filter.Result.NEUTRAL) {
437                    return r == Filter.Result.ACCEPT;
438                }
439            }
440            return level != null && intLevel >= level.intLevel();
441        }
442
443        boolean filter(final Level level, final Marker marker, final String msg, final Object p0) {
444            final Filter filter = config.getFilter();
445            if (filter != null) {
446                final Filter.Result r = filter.filter(logger, level, marker, msg, p0);
447                if (r != Filter.Result.NEUTRAL) {
448                    return r == Filter.Result.ACCEPT;
449                }
450            }
451            return level != null && intLevel >= level.intLevel();
452        }
453
454        boolean filter(final Level level, final Marker marker, final String msg, final Object p0,
455                final Object p1) {
456            final Filter filter = config.getFilter();
457            if (filter != null) {
458                final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1);
459                if (r != Filter.Result.NEUTRAL) {
460                    return r == Filter.Result.ACCEPT;
461                }
462            }
463            return level != null && intLevel >= level.intLevel();
464        }
465
466        boolean filter(final Level level, final Marker marker, final String msg, final Object p0,
467                final Object p1, final Object p2) {
468            final Filter filter = config.getFilter();
469            if (filter != null) {
470                final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2);
471                if (r != Filter.Result.NEUTRAL) {
472                    return r == Filter.Result.ACCEPT;
473                }
474            }
475            return level != null && intLevel >= level.intLevel();
476        }
477
478        boolean filter(final Level level, final Marker marker, final String msg, final Object p0,
479                final Object p1, final Object p2, final Object p3) {
480            final Filter filter = config.getFilter();
481            if (filter != null) {
482                final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3);
483                if (r != Filter.Result.NEUTRAL) {
484                    return r == Filter.Result.ACCEPT;
485                }
486            }
487            return level != null && intLevel >= level.intLevel();
488        }
489
490        boolean filter(final Level level, final Marker marker, final String msg, final Object p0,
491                final Object p1, final Object p2, final Object p3,
492                final Object p4) {
493            final Filter filter = config.getFilter();
494            if (filter != null) {
495                final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3, p4);
496                if (r != Filter.Result.NEUTRAL) {
497                    return r == Filter.Result.ACCEPT;
498                }
499            }
500            return level != null && intLevel >= level.intLevel();
501        }
502
503        boolean filter(final Level level, final Marker marker, final String msg, final Object p0,
504                final Object p1, final Object p2, final Object p3,
505                final Object p4, final Object p5) {
506            final Filter filter = config.getFilter();
507            if (filter != null) {
508                final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5);
509                if (r != Filter.Result.NEUTRAL) {
510                    return r == Filter.Result.ACCEPT;
511                }
512            }
513            return level != null && intLevel >= level.intLevel();
514        }
515
516        boolean filter(final Level level, final Marker marker, final String msg, final Object p0,
517                final Object p1, final Object p2, final Object p3,
518                final Object p4, final Object p5, final Object p6) {
519            final Filter filter = config.getFilter();
520            if (filter != null) {
521                final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6);
522                if (r != Filter.Result.NEUTRAL) {
523                    return r == Filter.Result.ACCEPT;
524                }
525            }
526            return level != null && intLevel >= level.intLevel();
527        }
528
529        boolean filter(final Level level, final Marker marker, final String msg, final Object p0,
530                final Object p1, final Object p2, final Object p3,
531                final Object p4, final Object p5, final Object p6,
532                final Object p7) {
533            final Filter filter = config.getFilter();
534            if (filter != null) {
535                final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6, p7);
536                if (r != Filter.Result.NEUTRAL) {
537                    return r == Filter.Result.ACCEPT;
538                }
539            }
540            return level != null && intLevel >= level.intLevel();
541        }
542
543        boolean filter(final Level level, final Marker marker, final String msg, final Object p0,
544                final Object p1, final Object p2, final Object p3,
545                final Object p4, final Object p5, final Object p6,
546                final Object p7, final Object p8) {
547            final Filter filter = config.getFilter();
548            if (filter != null) {
549                final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6, p7, p8);
550                if (r != Filter.Result.NEUTRAL) {
551                    return r == Filter.Result.ACCEPT;
552                }
553            }
554            return level != null && intLevel >= level.intLevel();
555        }
556
557        boolean filter(final Level level, final Marker marker, final String msg, final Object p0,
558                final Object p1, final Object p2, final Object p3,
559                final Object p4, final Object p5, final Object p6,
560                final Object p7, final Object p8, final Object p9) {
561            final Filter filter = config.getFilter();
562            if (filter != null) {
563                final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6, p7, p8,
564                        p9);
565                if (r != Filter.Result.NEUTRAL) {
566                    return r == Filter.Result.ACCEPT;
567                }
568            }
569            return level != null && intLevel >= level.intLevel();
570        }
571
572        boolean filter(final Level level, final Marker marker, final CharSequence msg, final Throwable t) {
573            final Filter filter = config.getFilter();
574            if (filter != null) {
575                final Filter.Result r = filter.filter(logger, level, marker, msg, t);
576                if (r != Filter.Result.NEUTRAL) {
577                    return r == Filter.Result.ACCEPT;
578                }
579            }
580            return level != null && intLevel >= level.intLevel();
581        }
582
583        boolean filter(final Level level, final Marker marker, final Object msg, final Throwable t) {
584            final Filter filter = config.getFilter();
585            if (filter != null) {
586                final Filter.Result r = filter.filter(logger, level, marker, msg, t);
587                if (r != Filter.Result.NEUTRAL) {
588                    return r == Filter.Result.ACCEPT;
589                }
590            }
591            return level != null && intLevel >= level.intLevel();
592        }
593
594        boolean filter(final Level level, final Marker marker, final Message msg, final Throwable t) {
595            final Filter filter = config.getFilter();
596            if (filter != null) {
597                final Filter.Result r = filter.filter(logger, level, marker, msg, t);
598                if (r != Filter.Result.NEUTRAL) {
599                    return r == Filter.Result.ACCEPT;
600                }
601            }
602            return level != null && intLevel >= level.intLevel();
603        }
604
605        @Override
606        public String toString() {
607            StringBuilder builder = new StringBuilder();
608            builder.append("PrivateConfig [loggerConfig=");
609            builder.append(loggerConfig);
610            builder.append(", config=");
611            builder.append(config);
612            builder.append(", loggerConfigLevel=");
613            builder.append(loggerConfigLevel);
614            builder.append(", intLevel=");
615            builder.append(intLevel);
616            builder.append(", logger=");
617            builder.append(logger);
618            builder.append("]");
619            return builder.toString();
620        }
621    }
622
623    /**
624     * Serialization proxy class for Logger. Since the LoggerContext and config information can be reconstructed on the
625     * fly, the only information needed for a Logger are what's available in AbstractLogger.
626     *
627     * @since 2.5
628     */
629    protected static class LoggerProxy implements Serializable {
630        private static final long serialVersionUID = 1L;
631
632        private final String name;
633        private final MessageFactory messageFactory;
634
635        public LoggerProxy(String name, MessageFactory messageFactory) {
636            this.name = name;
637            this.messageFactory = messageFactory;
638        }
639
640        protected Object readResolve() throws ObjectStreamException {
641            return new Logger(LoggerContext.getContext(), name, messageFactory);
642        }
643    }
644
645    /**
646     * Returns a String representation of this instance in the form {@code "name:level[ in context_name]"}.
647     *
648     * @return A String describing this Logger instance.
649     */
650    @Override
651    public String toString() {
652        final String nameLevel = Strings.EMPTY + getName() + ':' + getLevel();
653        if (context == null) {
654            return nameLevel;
655        }
656        final String contextName = context.getName();
657        return contextName == null ? nameLevel : nameLevel + " in " + contextName;
658    }
659
660    @Override
661    public boolean equals(final Object o) {
662        if (this == o) {
663            return true;
664        }
665        if (o == null || getClass() != o.getClass()) {
666            return false;
667        }
668        final Logger that = (Logger) o;
669        return getName().equals(that.getName());
670    }
671
672    @Override
673    public int hashCode() {
674        return getName().hashCode();
675    }
676}