001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.classic;
015
016import java.io.ObjectStreamException;
017import java.io.Serializable;
018import java.util.Collections;
019import java.util.Iterator;
020import java.util.List;
021import java.util.concurrent.CopyOnWriteArrayList;
022
023import org.slf4j.LoggerFactory;
024import org.slf4j.Marker;
025import org.slf4j.spi.DefaultLoggingEventBuilder;
026import org.slf4j.spi.LocationAwareLogger;
027import org.slf4j.spi.LoggingEventAware;
028import org.slf4j.spi.LoggingEventBuilder;
029
030import ch.qos.logback.classic.spi.ILoggingEvent;
031import ch.qos.logback.classic.spi.LoggingEvent;
032import ch.qos.logback.classic.util.LoggerNameUtil;
033import ch.qos.logback.core.Appender;
034import ch.qos.logback.core.CoreConstants;
035import ch.qos.logback.core.spi.AppenderAttachable;
036import ch.qos.logback.core.spi.AppenderAttachableImpl;
037import ch.qos.logback.core.spi.FilterReply;
038
039public final class Logger
040        implements org.slf4j.Logger, LocationAwareLogger, LoggingEventAware, AppenderAttachable<ILoggingEvent>, Serializable {
041
042    private static final long serialVersionUID = 5454405123156820674L; // 8745934908040027998L;
043
044    /**
045     * The fully qualified name of this class. Used in gathering caller information.
046     */
047    public static final String FQCN = ch.qos.logback.classic.Logger.class.getName();
048
049    /**
050     * The name of this logger
051     */
052    private String name;
053
054    // The assigned levelInt of this logger. Can be null.
055    transient private Level level;
056
057    // The effective levelInt is the assigned levelInt and if null, a levelInt is
058    // inherited form a parent.
059    transient private int effectiveLevelInt;
060
061    /**
062     * The parent of this category. All categories have at least one ancestor which
063     * is the root category.
064     */
065    transient private Logger parent;
066
067    /**
068     * The children of this logger. A logger may have zero or more children.
069     */
070    transient private List<Logger> childrenList;
071
072    /**
073     * It is assumed that once the 'aai' variable is set to a non-null value, it
074     * will never be reset to null. it is further assumed that only place where the
075     * 'aai variable is set is within the addAppender method. This method is
076     * synchronized on 'this' (Logger) protecting against simultaneous
077     * re-configuration of this logger (a very unlikely scenario).
078     * 
079     * <p>
080     * It is further assumed that the AppenderAttachableImpl is responsible for its
081     * internal synchronization and thread safety. Thus, we can get away with *not*
082     * synchronizing on the 'aai' (check null/ read) because
083     * <p>
084     * 1) the 'aai' variable is immutable once set to non-null
085     * <p>
086     * 2) 'aai' is getAndSet only within addAppender which is synchronized
087     * <p>
088     * 3) all the other methods check whether 'aai' is null
089     * <p>
090     * 4) AppenderAttachableImpl is thread safe
091     */
092    transient private AppenderAttachableImpl<ILoggingEvent> aai;
093    /**
094     * Additivity is set to true by default, that is children inherit the appenders
095     * of their ancestors by default. If this variable is set to <code>false</code>
096     * then the appenders located in the ancestors of this logger will not be used.
097     * However, the children of this logger will inherit its appenders, unless the
098     * children have their additivity flag set to <code>false</code> too. See the
099     * user manual for more details.
100     */
101    transient private boolean additive = true;
102
103    final transient LoggerContext loggerContext;
104
105    Logger(String name, Logger parent, LoggerContext loggerContext) {
106        this.name = name;
107        this.parent = parent;
108        this.loggerContext = loggerContext;
109    }
110
111    public Level getEffectiveLevel() {
112        return Level.toLevel(effectiveLevelInt);
113    }
114
115    int getEffectiveLevelInt() {
116        return effectiveLevelInt;
117    }
118
119    public Level getLevel() {
120        return level;
121    }
122
123    public String getName() {
124        return name;
125    }
126
127    private boolean isRootLogger() {
128        // only the root logger has a null parent
129        return parent == null;
130    }
131
132    Logger getChildByName(final String childName) {
133        if (childrenList == null) {
134            return null;
135        } else {
136            int len = this.childrenList.size();
137            for (int i = 0; i < len; i++) {
138                final Logger childLogger_i = (Logger) childrenList.get(i);
139                final String childName_i = childLogger_i.getName();
140
141                if (childName.equals(childName_i)) {
142                    return childLogger_i;
143                }
144            }
145            // no child found
146            return null;
147        }
148    }
149
150    public synchronized void setLevel(Level newLevel) {
151        if (level == newLevel) {
152            // nothing to do;
153            return;
154        }
155        if (newLevel == null && isRootLogger()) {
156            throw new IllegalArgumentException("The level of the root logger cannot be set to null");
157        }
158
159        level = newLevel;
160        if (newLevel == null) {
161            effectiveLevelInt = parent.effectiveLevelInt;
162            newLevel = parent.getEffectiveLevel();
163        } else {
164            effectiveLevelInt = newLevel.levelInt;
165        }
166
167        if (childrenList != null) {
168            int len = childrenList.size();
169            for (int i = 0; i < len; i++) {
170                Logger child = (Logger) childrenList.get(i);
171                // tell child to handle parent levelInt change
172                child.handleParentLevelChange(effectiveLevelInt);
173            }
174        }
175        // inform listeners
176        loggerContext.fireOnLevelChange(this, newLevel);
177    }
178
179    /**
180     * This method is invoked by parent logger to let this logger know that the
181     * prent's levelInt changed.
182     * 
183     * @param newParentLevelInt
184     */
185    private synchronized void handleParentLevelChange(int newParentLevelInt) {
186        // changes in the parent levelInt affect children only if their levelInt is
187        // null
188        if (level == null) {
189            effectiveLevelInt = newParentLevelInt;
190
191            // propagate the parent levelInt change to this logger's children
192            if (childrenList != null) {
193                int len = childrenList.size();
194                for (int i = 0; i < len; i++) {
195                    Logger child = (Logger) childrenList.get(i);
196                    child.handleParentLevelChange(newParentLevelInt);
197                }
198            }
199        }
200    }
201
202    /**
203     * Remove all previously added appenders from this logger instance.
204     * <p/>
205     * This is useful when re-reading configuration information.
206     */
207    public void detachAndStopAllAppenders() {
208        if (aai != null) {
209            aai.detachAndStopAllAppenders();
210        }
211    }
212
213    public boolean detachAppender(String name) {
214        if (aai == null) {
215            return false;
216        }
217        return aai.detachAppender(name);
218    }
219
220    // this method MUST be synchronized. See comments on 'aai' field for further
221    // details.
222    public synchronized void addAppender(Appender<ILoggingEvent> newAppender) {
223        if (aai == null) {
224            aai = new AppenderAttachableImpl<ILoggingEvent>();
225        }
226        aai.addAppender(newAppender);
227    }
228
229    public boolean isAttached(Appender<ILoggingEvent> appender) {
230        if (aai == null) {
231            return false;
232        }
233        return aai.isAttached(appender);
234    }
235
236    @SuppressWarnings("unchecked")
237    public Iterator<Appender<ILoggingEvent>> iteratorForAppenders() {
238        if (aai == null) {
239            return Collections.EMPTY_LIST.iterator();
240        }
241        return aai.iteratorForAppenders();
242    }
243
244    public Appender<ILoggingEvent> getAppender(String name) {
245        if (aai == null) {
246            return null;
247        }
248        return aai.getAppender(name);
249    }
250
251    /**
252     * Invoke all the appenders of this logger.
253     * 
254     * @param event The event to log
255     */
256    public void callAppenders(ILoggingEvent event) {
257        int writes = 0;
258        for (Logger l = this; l != null; l = l.parent) {
259            writes += l.appendLoopOnAppenders(event);
260            if (!l.additive) {
261                break;
262            }
263        }
264        // No appenders in hierarchy
265        if (writes == 0) {
266            loggerContext.noAppenderDefinedWarning(this);
267        }
268    }
269
270    private int appendLoopOnAppenders(ILoggingEvent event) {
271        if (aai != null) {
272            return aai.appendLoopOnAppenders(event);
273        } else {
274            return 0;
275        }
276    }
277
278    /**
279     * Remove the appender passed as parameter form the list of appenders.
280     */
281    public boolean detachAppender(Appender<ILoggingEvent> appender) {
282        if (aai == null) {
283            return false;
284        }
285        return aai.detachAppender(appender);
286    }
287
288    /**
289     * Create a child of this logger by suffix, that is, the part of the name
290     * extending this logger. For example, if this logger is named "x.y" and the
291     * lastPart is "z", then the created child logger will be named "x.y.z".
292     * 
293     * <p>
294     * IMPORTANT: Calls to this method must be within a synchronized block on this
295     * logger.
296     * 
297     * @param lastPart the suffix (i.e. last part) of the child logger name. This
298     *                 parameter may not include dots, i.e. the logger separator
299     *                 character.
300     * @return
301     */
302    Logger createChildByLastNamePart(final String lastPart) {
303        int i_index = LoggerNameUtil.getFirstSeparatorIndexOf(lastPart);
304        if (i_index != -1) {
305            throw new IllegalArgumentException(
306                    "Child name [" + lastPart + " passed as parameter, may not include [" + CoreConstants.DOT + "]");
307        }
308
309        if (childrenList == null) {
310            childrenList = new CopyOnWriteArrayList<Logger>();
311        }
312        Logger childLogger;
313        if (this.isRootLogger()) {
314            childLogger = new Logger(lastPart, this, this.loggerContext);
315        } else {
316            childLogger = new Logger(name + CoreConstants.DOT + lastPart, this, this.loggerContext);
317        }
318        childrenList.add(childLogger);
319        childLogger.effectiveLevelInt = this.effectiveLevelInt;
320        return childLogger;
321    }
322
323    private void localLevelReset() {
324        effectiveLevelInt = Level.DEBUG_INT;
325        if (isRootLogger()) {
326            level = Level.DEBUG;
327        } else {
328            level = null;
329        }
330    }
331
332    void recursiveReset() {
333        detachAndStopAllAppenders();
334        localLevelReset();
335        additive = true;
336        if (childrenList == null) {
337            return;
338        }
339        for (Logger childLogger : childrenList) {
340            childLogger.recursiveReset();
341        }
342    }
343
344    /**
345     * The default size of child list arrays. The JDK 1.5 default is 10. We use a
346     * smaller value to save a little space.
347     */
348
349    Logger createChildByName(final String childName) {
350        int i_index = LoggerNameUtil.getSeparatorIndexOf(childName, this.name.length() + 1);
351        if (i_index != -1) {
352            throw new IllegalArgumentException("For logger [" + this.name + "] child name [" + childName
353                    + " passed as parameter, may not include '.' after index" + (this.name.length() + 1));
354        }
355
356        if (childrenList == null) {
357            childrenList = new CopyOnWriteArrayList<Logger>();
358        }
359        Logger childLogger;
360        childLogger = new Logger(childName, this, this.loggerContext);
361        childrenList.add(childLogger);
362        childLogger.effectiveLevelInt = this.effectiveLevelInt;
363        return childLogger;
364    }
365
366    /**
367     * The next methods are not merged into one because of the time we gain by not
368     * creating a new Object[] with the params. This reduces the cost of not logging
369     * by about 20 nanoseconds.
370     */
371
372    // for 0 or 3 or more parameters
373    private void filterAndLog_0_Or3Plus(final String localFQCN, final Marker marker, final Level level,
374            final String msg, final Object[] params, final Throwable t) {
375
376        final FilterReply decision = loggerContext.getTurboFilterChainDecision_0_3OrMore(marker, this, level, msg,
377                params, t);
378
379        // the ACCEPT case falls through
380        if (decision == FilterReply.NEUTRAL) {
381            if (effectiveLevelInt > level.levelInt) {
382                return;
383            }
384        } else if (decision == FilterReply.DENY) {
385            return;
386        }
387
388        buildLoggingEventAndAppend(localFQCN, marker, level, msg, params, t);
389    }
390
391
392    private void filterAndLog_1(final String localFQCN, final Marker marker, final Level level, final String msg,
393            final Object param, final Throwable t) {
394
395        final FilterReply decision = loggerContext.getTurboFilterChainDecision_1(marker, this, level, msg, param, t);
396
397        // the ACCEPT case falls through
398        if (decision == FilterReply.NEUTRAL) {
399            if (effectiveLevelInt > level.levelInt) {
400                return;
401            }
402        } else if (decision == FilterReply.DENY) {
403            return;
404        }
405
406        buildLoggingEventAndAppend(localFQCN, marker, level, msg, new Object[] { param }, t);
407    }
408
409    private void filterAndLog_2(final String localFQCN, final Marker marker, final Level level, final String msg,
410            final Object param1, final Object param2, final Throwable t) {
411
412        final FilterReply decision = loggerContext.getTurboFilterChainDecision_2(marker, this, level, msg, param1,
413                param2, t);
414
415        // the ACCEPT case falls through
416        if (decision == FilterReply.NEUTRAL) {
417            if (effectiveLevelInt > level.levelInt) {
418                return;
419            }
420        } else if (decision == FilterReply.DENY) {
421            return;
422        }
423
424        buildLoggingEventAndAppend(localFQCN, marker, level, msg, new Object[] { param1, param2 }, t);
425    }
426
427    private void buildLoggingEventAndAppend(final String localFQCN, final Marker marker, final Level level,
428            final String msg, final Object[] params, final Throwable t) {
429        LoggingEvent le = new LoggingEvent(localFQCN, this, level, msg, t, params);
430        le.addMarker(marker);
431        callAppenders(le);
432    }
433
434    public void trace(String msg) {
435        filterAndLog_0_Or3Plus(FQCN, null, Level.TRACE, msg, null, null);
436    }
437
438    public void trace(String format, Object arg) {
439        filterAndLog_1(FQCN, null, Level.TRACE, format, arg, null);
440    }
441
442    public void trace(String format, Object arg1, Object arg2) {
443        filterAndLog_2(FQCN, null, Level.TRACE, format, arg1, arg2, null);
444    }
445
446    public void trace(String format, Object... argArray) {
447        filterAndLog_0_Or3Plus(FQCN, null, Level.TRACE, format, argArray, null);
448    }
449
450    public void trace(String msg, Throwable t) {
451        filterAndLog_0_Or3Plus(FQCN, null, Level.TRACE, msg, null, t);
452    }
453
454    public void trace(Marker marker, String msg) {
455        filterAndLog_0_Or3Plus(FQCN, marker, Level.TRACE, msg, null, null);
456    }
457
458    public void trace(Marker marker, String format, Object arg) {
459        filterAndLog_1(FQCN, marker, Level.TRACE, format, arg, null);
460    }
461
462    public void trace(Marker marker, String format, Object arg1, Object arg2) {
463        filterAndLog_2(FQCN, marker, Level.TRACE, format, arg1, arg2, null);
464    }
465
466    public void trace(Marker marker, String format, Object... argArray) {
467        filterAndLog_0_Or3Plus(FQCN, marker, Level.TRACE, format, argArray, null);
468    }
469
470    public void trace(Marker marker, String msg, Throwable t) {
471        filterAndLog_0_Or3Plus(FQCN, marker, Level.TRACE, msg, null, t);
472    }
473
474    public boolean isDebugEnabled() {
475        return isDebugEnabled(null);
476    }
477
478    public boolean isDebugEnabled(Marker marker) {
479        final FilterReply decision = callTurboFilters(marker, Level.DEBUG);
480        if (decision == FilterReply.NEUTRAL) {
481            return effectiveLevelInt <= Level.DEBUG_INT;
482        } else if (decision == FilterReply.DENY) {
483            return false;
484        } else if (decision == FilterReply.ACCEPT) {
485            return true;
486        } else {
487            throw new IllegalStateException("Unknown FilterReply value: " + decision);
488        }
489    }
490
491    public void debug(String msg) {
492        filterAndLog_0_Or3Plus(FQCN, null, Level.DEBUG, msg, null, null);
493    }
494
495    public void debug(String format, Object arg) {
496        filterAndLog_1(FQCN, null, Level.DEBUG, format, arg, null);
497    }
498
499    public void debug(String format, Object arg1, Object arg2) {
500        filterAndLog_2(FQCN, null, Level.DEBUG, format, arg1, arg2, null);
501    }
502
503    public void debug(String format, Object... argArray) {
504        filterAndLog_0_Or3Plus(FQCN, null, Level.DEBUG, format, argArray, null);
505    }
506
507    public void debug(String msg, Throwable t) {
508        filterAndLog_0_Or3Plus(FQCN, null, Level.DEBUG, msg, null, t);
509    }
510
511    public void debug(Marker marker, String msg) {
512        filterAndLog_0_Or3Plus(FQCN, marker, Level.DEBUG, msg, null, null);
513    }
514
515    public void debug(Marker marker, String format, Object arg) {
516        filterAndLog_1(FQCN, marker, Level.DEBUG, format, arg, null);
517    }
518
519    public void debug(Marker marker, String format, Object arg1, Object arg2) {
520        filterAndLog_2(FQCN, marker, Level.DEBUG, format, arg1, arg2, null);
521    }
522
523    public void debug(Marker marker, String format, Object... argArray) {
524        filterAndLog_0_Or3Plus(FQCN, marker, Level.DEBUG, format, argArray, null);
525    }
526
527    public void debug(Marker marker, String msg, Throwable t) {
528        filterAndLog_0_Or3Plus(FQCN, marker, Level.DEBUG, msg, null, t);
529    }
530
531    public void error(String msg) {
532        filterAndLog_0_Or3Plus(FQCN, null, Level.ERROR, msg, null, null);
533    }
534
535    public void error(String format, Object arg) {
536        filterAndLog_1(FQCN, null, Level.ERROR, format, arg, null);
537    }
538
539    public void error(String format, Object arg1, Object arg2) {
540        filterAndLog_2(FQCN, null, Level.ERROR, format, arg1, arg2, null);
541    }
542
543    public void error(String format, Object... argArray) {
544        filterAndLog_0_Or3Plus(FQCN, null, Level.ERROR, format, argArray, null);
545    }
546
547    public void error(String msg, Throwable t) {
548        filterAndLog_0_Or3Plus(FQCN, null, Level.ERROR, msg, null, t);
549    }
550
551    public void error(Marker marker, String msg) {
552        filterAndLog_0_Or3Plus(FQCN, marker, Level.ERROR, msg, null, null);
553    }
554
555    public void error(Marker marker, String format, Object arg) {
556        filterAndLog_1(FQCN, marker, Level.ERROR, format, arg, null);
557    }
558
559    public void error(Marker marker, String format, Object arg1, Object arg2) {
560        filterAndLog_2(FQCN, marker, Level.ERROR, format, arg1, arg2, null);
561    }
562
563    public void error(Marker marker, String format, Object... argArray) {
564        filterAndLog_0_Or3Plus(FQCN, marker, Level.ERROR, format, argArray, null);
565    }
566
567    public void error(Marker marker, String msg, Throwable t) {
568        filterAndLog_0_Or3Plus(FQCN, marker, Level.ERROR, msg, null, t);
569    }
570
571    public boolean isInfoEnabled() {
572        return isInfoEnabled(null);
573    }
574
575    public boolean isInfoEnabled(Marker marker) {
576        FilterReply decision = callTurboFilters(marker, Level.INFO);
577        if (decision == FilterReply.NEUTRAL) {
578            return effectiveLevelInt <= Level.INFO_INT;
579        } else if (decision == FilterReply.DENY) {
580            return false;
581        } else if (decision == FilterReply.ACCEPT) {
582            return true;
583        } else {
584            throw new IllegalStateException("Unknown FilterReply value: " + decision);
585        }
586    }
587
588    public void info(String msg) {
589        filterAndLog_0_Or3Plus(FQCN, null, Level.INFO, msg, null, null);
590    }
591
592    public void info(String format, Object arg) {
593        filterAndLog_1(FQCN, null, Level.INFO, format, arg, null);
594    }
595
596    public void info(String format, Object arg1, Object arg2) {
597        filterAndLog_2(FQCN, null, Level.INFO, format, arg1, arg2, null);
598    }
599
600    public void info(String format, Object... argArray) {
601        filterAndLog_0_Or3Plus(FQCN, null, Level.INFO, format, argArray, null);
602    }
603
604    public void info(String msg, Throwable t) {
605        filterAndLog_0_Or3Plus(FQCN, null, Level.INFO, msg, null, t);
606    }
607
608    public void info(Marker marker, String msg) {
609        filterAndLog_0_Or3Plus(FQCN, marker, Level.INFO, msg, null, null);
610    }
611
612    public void info(Marker marker, String format, Object arg) {
613        filterAndLog_1(FQCN, marker, Level.INFO, format, arg, null);
614    }
615
616    public void info(Marker marker, String format, Object arg1, Object arg2) {
617        filterAndLog_2(FQCN, marker, Level.INFO, format, arg1, arg2, null);
618    }
619
620    public void info(Marker marker, String format, Object... argArray) {
621        filterAndLog_0_Or3Plus(FQCN, marker, Level.INFO, format, argArray, null);
622    }
623
624    public void info(Marker marker, String msg, Throwable t) {
625        filterAndLog_0_Or3Plus(FQCN, marker, Level.INFO, msg, null, t);
626    }
627
628    public boolean isTraceEnabled() {
629        return isTraceEnabled(null);
630    }
631
632    public boolean isTraceEnabled(Marker marker) {
633        final FilterReply decision = callTurboFilters(marker, Level.TRACE);
634        if (decision == FilterReply.NEUTRAL) {
635            return effectiveLevelInt <= Level.TRACE_INT;
636        } else if (decision == FilterReply.DENY) {
637            return false;
638        } else if (decision == FilterReply.ACCEPT) {
639            return true;
640        } else {
641            throw new IllegalStateException("Unknown FilterReply value: " + decision);
642        }
643    }
644
645    public boolean isErrorEnabled() {
646        return isErrorEnabled(null);
647    }
648
649    public boolean isErrorEnabled(Marker marker) {
650        FilterReply decision = callTurboFilters(marker, Level.ERROR);
651        if (decision == FilterReply.NEUTRAL) {
652            return effectiveLevelInt <= Level.ERROR_INT;
653        } else if (decision == FilterReply.DENY) {
654            return false;
655        } else if (decision == FilterReply.ACCEPT) {
656            return true;
657        } else {
658            throw new IllegalStateException("Unknown FilterReply value: " + decision);
659        }
660    }
661
662    public boolean isWarnEnabled() {
663        return isWarnEnabled(null);
664    }
665
666    public boolean isWarnEnabled(Marker marker) {
667        FilterReply decision = callTurboFilters(marker, Level.WARN);
668        if (decision == FilterReply.NEUTRAL) {
669            return effectiveLevelInt <= Level.WARN_INT;
670        } else if (decision == FilterReply.DENY) {
671            return false;
672        } else if (decision == FilterReply.ACCEPT) {
673            return true;
674        } else {
675            throw new IllegalStateException("Unknown FilterReply value: " + decision);
676        }
677
678    }
679
680    public boolean isEnabledFor(Marker marker, Level level) {
681        FilterReply decision = callTurboFilters(marker, level);
682        if (decision == FilterReply.NEUTRAL) {
683            return effectiveLevelInt <= level.levelInt;
684        } else if (decision == FilterReply.DENY) {
685            return false;
686        } else if (decision == FilterReply.ACCEPT) {
687            return true;
688        } else {
689            throw new IllegalStateException("Unknown FilterReply value: " + decision);
690        }
691    }
692
693    public boolean isEnabledFor(Level level) {
694        return isEnabledFor(null, level);
695    }
696
697    public void warn(String msg) {
698        filterAndLog_0_Or3Plus(FQCN, null, Level.WARN, msg, null, null);
699    }
700
701    public void warn(String msg, Throwable t) {
702        filterAndLog_0_Or3Plus(FQCN, null, Level.WARN, msg, null, t);
703    }
704
705    public void warn(String format, Object arg) {
706        filterAndLog_1(FQCN, null, Level.WARN, format, arg, null);
707    }
708
709    public void warn(String format, Object arg1, Object arg2) {
710        filterAndLog_2(FQCN, null, Level.WARN, format, arg1, arg2, null);
711    }
712
713    public void warn(String format, Object... argArray) {
714        filterAndLog_0_Or3Plus(FQCN, null, Level.WARN, format, argArray, null);
715    }
716
717    public void warn(Marker marker, String msg) {
718        filterAndLog_0_Or3Plus(FQCN, marker, Level.WARN, msg, null, null);
719    }
720
721    public void warn(Marker marker, String format, Object arg) {
722        filterAndLog_1(FQCN, marker, Level.WARN, format, arg, null);
723    }
724
725    public void warn(Marker marker, String format, Object... argArray) {
726        filterAndLog_0_Or3Plus(FQCN, marker, Level.WARN, format, argArray, null);
727    }
728
729    public void warn(Marker marker, String format, Object arg1, Object arg2) {
730        filterAndLog_2(FQCN, marker, Level.WARN, format, arg1, arg2, null);
731    }
732
733    public void warn(Marker marker, String msg, Throwable t) {
734        filterAndLog_0_Or3Plus(FQCN, marker, Level.WARN, msg, null, t);
735    }
736
737    public boolean isAdditive() {
738        return additive;
739    }
740
741    public void setAdditive(boolean additive) {
742        this.additive = additive;
743    }
744
745    public String toString() {
746        return "Logger[" + name + "]";
747    }
748
749    /**
750     * Method that calls the attached TurboFilter objects based on the logger and
751     * the level.
752     * 
753     * <p>It is used by isXYZEnabled() methods such as {@link #isDebugEnabled()},
754     * {@link #isInfoEnabled()} etc.
755     * </p>
756     *
757     * <p>It returns the typical FilterReply values: ACCEPT, NEUTRAL or DENY.
758     * </p>
759     * @param level
760     * @return the reply given by the TurboFilters
761     */
762    private FilterReply callTurboFilters(Marker marker, Level level) {
763        return loggerContext.getTurboFilterChainDecision_0_3OrMore(marker, this, level, null, null, null);
764    }
765
766    /**
767     * Method that calls the attached TurboFilter objects based on this logger and
768     * {@link org.slf4j.event.LoggingEvent  LoggingEvent}.
769     *
770     * <p>This method is typically called by
771     * {@link #log(org.slf4j.event.LoggingEvent) log(LoggingEvent)} method.</p>
772     *
773     * <p>It returns {@link FilterReply} values: ACCEPT, NEUTRAL or DENY.
774     * </p>
775     *
776     * @param slf4jEvent the SLF4J LoggingEvent
777     * @return the reply given by the TurboFilters
778     */
779    private FilterReply callTurboFilters(org.slf4j.event.LoggingEvent slf4jEvent) {
780        return loggerContext.getTurboFilterChainDecision(this, slf4jEvent);
781    }
782
783
784    /**
785     * Return the context for this logger.
786     * 
787     * @return the context
788     */
789    public LoggerContext getLoggerContext() {
790        return loggerContext;
791    }
792
793    /**
794     * Creates a {@link LoggingEventBuilder} of type {@link DefaultLoggingEventBuilder}.
795     * 
796     * @since 1.3
797     */
798    @Override
799    public LoggingEventBuilder makeLoggingEventBuilder(org.slf4j.event.Level level) {
800        return new DefaultLoggingEventBuilder(this, level);
801    }
802
803    public void log(Marker marker, String fqcn, int levelInt, String message, Object[] argArray, Throwable t) {
804        Level level = Level.fromLocationAwareLoggerInteger(levelInt);
805        filterAndLog_0_Or3Plus(fqcn, marker, level, message, argArray, t);
806    }
807
808    /**
809     * Support SLF4J interception during initialization as introduced in SLF4J
810     * version 1.7.15. Alternatively, this method can be called by SLF4J's fluent API, i.e. by
811     * {@link LoggingEventBuilder}.
812     *
813     * 
814     * @since 1.1.4
815     * @param slf4jEvent
816     */
817    public void log(org.slf4j.event.LoggingEvent slf4jEvent) {
818        org.slf4j.event.Level slf4jLevel = slf4jEvent.getLevel();
819        Level logbackLevel = Level.convertAnSLF4JLevel(slf4jLevel);
820
821        // invoke turbo filters. See also https://github.com/qos-ch/logback/issues/871
822        final FilterReply decision = loggerContext.getTurboFilterChainDecision(this, slf4jEvent);
823        // the ACCEPT and NEUTRAL cases falls through as there are no further level checks to be done
824        if (decision == FilterReply.DENY) {
825            return;
826        }
827
828        // By default, assume this class was the caller. In some cases, {@link SubstituteLogger} can also be a caller.
829        //
830        // It is possible that the caller is some other library, e.g. slf4j-jdk-platform-logging
831
832        String callerBoundary = slf4jEvent.getCallerBoundary();
833        if (callerBoundary == null) {
834            callerBoundary = FQCN;
835        }
836
837        LoggingEvent lle = new LoggingEvent(callerBoundary, this, logbackLevel, slf4jEvent.getMessage(),
838                slf4jEvent.getThrowable(), slf4jEvent.getArgumentArray());
839        List<Marker> markers = slf4jEvent.getMarkers();
840
841        if (markers != null) {
842            markers.forEach(m -> lle.addMarker(m));
843        }
844
845        lle.setKeyValuePairs(slf4jEvent.getKeyValuePairs());
846
847        // Note that at this point, any calls made with a logger disabled
848        // for a given level, will be already filtered out/in. TurboFilters cannot
849        // act at this point in the process.
850        this.callAppenders(lle);
851    }
852
853    /**
854     * After serialization, the logger instance does not know its LoggerContext. The
855     * best we can do here, is to return a logger with the same name returned by
856     * org.slf4j.LoggerFactory.
857     * 
858     * @return Logger instance with the same name
859     * @throws ObjectStreamException
860     */
861    protected Object readResolve() throws ObjectStreamException {
862        return LoggerFactory.getLogger(getName());
863    }
864}