View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core;
18  
19  import java.io.ObjectStreamException;
20  import java.io.Serializable;
21  import java.util.ArrayList;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.logging.log4j.Level;
27  import org.apache.logging.log4j.Marker;
28  import org.apache.logging.log4j.core.config.Configuration;
29  import org.apache.logging.log4j.core.config.LoggerConfig;
30  import org.apache.logging.log4j.core.config.ReliabilityStrategy;
31  import org.apache.logging.log4j.core.filter.CompositeFilter;
32  import org.apache.logging.log4j.message.Message;
33  import org.apache.logging.log4j.message.MessageFactory;
34  import org.apache.logging.log4j.message.SimpleMessage;
35  import org.apache.logging.log4j.spi.AbstractLogger;
36  import org.apache.logging.log4j.util.Strings;
37  import org.apache.logging.log4j.util.Supplier;
38  
39  /**
40   * The core implementation of the {@link org.apache.logging.log4j.Logger} interface. Besides providing an implementation
41   * of all the Logger methods, this class also provides some convenience methods for Log4j 1.x compatibility as well as
42   * access to the {@link org.apache.logging.log4j.core.Filter Filters} and {@link org.apache.logging.log4j.core.Appender
43   * Appenders} associated with this Logger. Note that access to these underlying objects is provided primarily for use in
44   * unit tests or bridging legacy Log4j 1.x code. Future versions of this class may or may not include the various
45   * methods that are noted as not being part of the public API.
46   *
47   * TODO All the isEnabled methods could be pushed into a filter interface. Not sure of the utility of having isEnabled
48   * be able to examine the message pattern and parameters. (RG) Moving the isEnabled methods out of Logger noticeably
49   * impacts performance. The message pattern and parameters are required so that they can be used in global filters.
50   */
51  public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
52  
53      private static final long serialVersionUID = 1L;
54  
55      /**
56       * Config should be consistent across threads.
57       */
58      protected volatile PrivateConfig privateConfig;
59  
60      // FIXME: ditto to the above
61      private final LoggerContext context;
62  
63      /**
64       * The constructor.
65       *
66       * @param context The LoggerContext this Logger is associated with.
67       * @param messageFactory The message factory.
68       * @param name The name of the Logger.
69       */
70      protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
71          super(name, messageFactory);
72          this.context = context;
73          privateConfig = new PrivateConfig(context.getConfiguration(), this);
74      }
75  
76      protected Object writeReplace() throws ObjectStreamException {
77          return new LoggerProxy(getName(), getMessageFactory());
78      }
79  
80      /**
81       * This method is only used for 1.x compatibility. Returns the parent of this Logger. If it doesn't already exist
82       * return a temporary Logger.
83       *
84       * @return The parent Logger.
85       */
86      public Logger getParent() {
87          final LoggerConfig lc = privateConfig.loggerConfig.getName().equals(getName()) ? privateConfig.loggerConfig
88                  .getParent() : privateConfig.loggerConfig;
89          if (lc == null) {
90              return null;
91          }
92          final String lcName = lc.getName();
93          final MessageFactory messageFactory = getMessageFactory();
94          if (context.hasLogger(lcName, messageFactory)) {
95              return context.getLogger(lcName, messageFactory);
96          }
97          return new Logger(context, lcName, messageFactory);
98      }
99  
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             final 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(final String name, final 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 }