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.LocationAwareReliabilityStrategy;
30  import org.apache.logging.log4j.core.config.LoggerConfig;
31  import org.apache.logging.log4j.core.config.ReliabilityStrategy;
32  import org.apache.logging.log4j.core.filter.CompositeFilter;
33  import org.apache.logging.log4j.message.Message;
34  import org.apache.logging.log4j.message.MessageFactory;
35  import org.apache.logging.log4j.message.SimpleMessage;
36  import org.apache.logging.log4j.spi.AbstractLogger;
37  import org.apache.logging.log4j.util.Strings;
38  import org.apache.logging.log4j.util.Supplier;
39  
40  /**
41   * The core implementation of the {@link org.apache.logging.log4j.Logger} interface. Besides providing an implementation
42   * of all the Logger methods, this class also provides some convenience methods for Log4j 1.x compatibility as well as
43   * access to the {@link org.apache.logging.log4j.core.Filter Filters} and {@link org.apache.logging.log4j.core.Appender
44   * Appenders} associated with this Logger. Note that access to these underlying objects is provided primarily for use in
45   * unit tests or bridging legacy Log4j 1.x code. Future versions of this class may or may not include the various
46   * methods that are noted as not being part of the public API.
47   *
48   * TODO All the isEnabled methods could be pushed into a filter interface. Not sure of the utility of having isEnabled
49   * be able to examine the message pattern and parameters. (RG) Moving the isEnabled methods out of Logger noticeably
50   * impacts performance. The message pattern and parameters are required so that they can be used in global filters.
51   */
52  public class Logger extends AbstractLogger implements Supplier<LoggerConfig> {
53  
54      private static final long serialVersionUID = 1L;
55  
56      /**
57       * Config should be consistent across threads.
58       */
59      protected volatile PrivateConfig privateConfig;
60  
61      // FIXME: ditto to the above
62      private final LoggerContext context;
63  
64      /**
65       * The constructor.
66       *
67       * @param context The LoggerContext this Logger is associated with.
68       * @param messageFactory The message factory.
69       * @param name The name of the Logger.
70       */
71      protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
72          super(name, messageFactory);
73          this.context = context;
74          privateConfig = new PrivateConfig(context.getConfiguration(), this);
75      }
76  
77      protected Object writeReplace() throws ObjectStreamException {
78          return new LoggerProxy(getName(), getMessageFactory());
79      }
80  
81      /**
82       * This method is only used for 1.x compatibility. Returns the parent of this Logger. If it doesn't already exist
83       * return a temporary Logger.
84       *
85       * @return The parent Logger.
86       */
87      public Logger getParent() {
88          final LoggerConfig lc = privateConfig.loggerConfig.getName().equals(getName()) ? privateConfig.loggerConfig
89                  .getParent() : privateConfig.loggerConfig;
90          if (lc == null) {
91              return null;
92          }
93          final String lcName = lc.getName();
94          final MessageFactory messageFactory = getMessageFactory();
95          if (context.hasLogger(lcName, messageFactory)) {
96              return context.getLogger(lcName, messageFactory);
97          }
98          return new Logger(context, lcName, messageFactory);
99      }
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 }