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