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