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