001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.config;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025
026import org.apache.logging.log4j.Level;
027import org.apache.logging.log4j.LogManager;
028import org.apache.logging.log4j.Marker;
029import org.apache.logging.log4j.core.Appender;
030import org.apache.logging.log4j.core.Core;
031import org.apache.logging.log4j.core.Filter;
032import org.apache.logging.log4j.core.LogEvent;
033import org.apache.logging.log4j.core.LoggerContext;
034import org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender;
035import org.apache.logging.log4j.core.appender.FileAppender;
036import org.apache.logging.log4j.core.async.AsyncLoggerConfig;
037import org.apache.logging.log4j.core.async.AsyncLoggerContext;
038import org.apache.logging.log4j.core.async.AsyncLoggerContextSelector;
039import org.apache.logging.log4j.core.config.plugins.Plugin;
040import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
041import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
042import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
043import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
044import org.apache.logging.log4j.core.config.plugins.PluginElement;
045import org.apache.logging.log4j.core.config.plugins.PluginFactory;
046import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
047import org.apache.logging.log4j.core.config.properties.PropertiesConfiguration;
048import org.apache.logging.log4j.core.filter.AbstractFilterable;
049import org.apache.logging.log4j.core.impl.DefaultLogEventFactory;
050import org.apache.logging.log4j.core.impl.LocationAware;
051import org.apache.logging.log4j.core.impl.LocationAwareLogEventFactory;
052import org.apache.logging.log4j.core.impl.Log4jLogEvent;
053import org.apache.logging.log4j.core.impl.LogEventFactory;
054import org.apache.logging.log4j.core.impl.ReusableLogEventFactory;
055import org.apache.logging.log4j.core.lookup.StrSubstitutor;
056import org.apache.logging.log4j.core.util.Booleans;
057import org.apache.logging.log4j.core.util.Constants;
058import org.apache.logging.log4j.core.util.Loader;
059import org.apache.logging.log4j.message.Message;
060import org.apache.logging.log4j.util.PerformanceSensitive;
061import org.apache.logging.log4j.util.PropertiesUtil;
062import org.apache.logging.log4j.util.StackLocatorUtil;
063import org.apache.logging.log4j.util.Strings;
064
065/**
066 * Logger object that is created via configuration.
067 */
068@Plugin(name = "logger", category = Node.CATEGORY, printObject = true)
069public class LoggerConfig extends AbstractFilterable implements LocationAware {
070
071    public static final String ROOT = "root";
072    private static LogEventFactory LOG_EVENT_FACTORY = null;
073
074    private List<AppenderRef> appenderRefs = new ArrayList<>();
075    private final AppenderControlArraySet appenders = new AppenderControlArraySet();
076    private final String name;
077    private LogEventFactory logEventFactory;
078    private Level level;
079    private boolean additive = true;
080    private boolean includeLocation = true;
081    private LoggerConfig parent;
082    private Map<Property, Boolean> propertiesMap;
083    private final List<Property> properties;
084    private final boolean propertiesRequireLookup;
085    private final Configuration config;
086    private final ReliabilityStrategy reliabilityStrategy;
087
088    static {
089        final String factory = PropertiesUtil.getProperties().getStringProperty(Constants.LOG4J_LOG_EVENT_FACTORY);
090        if (factory != null) {
091            try {
092                final Class<?> clazz = Loader.loadClass(factory);
093                if (clazz != null && LogEventFactory.class.isAssignableFrom(clazz)) {
094                    LOG_EVENT_FACTORY = (LogEventFactory) clazz.newInstance();
095                }
096            } catch (final Exception ex) {
097                LOGGER.error("Unable to create LogEventFactory {}", factory, ex);
098            }
099        }
100        if (LOG_EVENT_FACTORY == null) {
101            LOG_EVENT_FACTORY = Constants.ENABLE_THREADLOCALS
102                    ? new ReusableLogEventFactory()
103                    : new DefaultLogEventFactory();
104        }
105    }
106
107    @PluginBuilderFactory
108    public static <B extends Builder<B>> B newBuilder() {
109        return new Builder<B>().asBuilder();
110    }
111
112    /**
113     * Builds LoggerConfig instances.
114     *
115     * @param <B>
116     *            The type to build
117     */
118    public static class Builder<B extends Builder<B>>
119            implements org.apache.logging.log4j.core.util.Builder<LoggerConfig> {
120
121        @PluginBuilderAttribute
122        private Boolean additivity;
123        @PluginBuilderAttribute
124        private Level level;
125        @PluginBuilderAttribute
126        private String levelAndRefs;
127        @PluginBuilderAttribute("name")
128        @Required(message = "Loggers cannot be configured without a name")
129        private String loggerName;
130        @PluginBuilderAttribute
131        private String includeLocation;
132        @PluginElement("AppenderRef")
133        private AppenderRef[] refs;
134        @PluginElement("Properties")
135        private Property[] properties;
136        @PluginConfiguration
137        private Configuration config;
138        @PluginElement("Filter")
139        private Filter filter;
140
141        public boolean isAdditivity() {
142            return additivity == null || additivity;
143        }
144
145        public B withAdditivity(boolean additivity) {
146            this.additivity = additivity;
147            return asBuilder();
148        }
149
150        public Level getLevel() {
151            return level;
152        }
153
154        public B withLevel(Level level) {
155            this.level = level;
156            return asBuilder();
157        }
158
159        public String getLevelAndRefs() {
160            return levelAndRefs;
161        }
162
163        public B withLevelAndRefs(String levelAndRefs) {
164            this.levelAndRefs = levelAndRefs;
165            return asBuilder();
166        }
167
168        public String getLoggerName() {
169            return loggerName;
170        }
171
172        public B withLoggerName(String loggerName) {
173            this.loggerName = loggerName;
174            return asBuilder();
175        }
176
177        public String getIncludeLocation() {
178            return includeLocation;
179        }
180
181        public B withIncludeLocation(String includeLocation) {
182            this.includeLocation = includeLocation;
183            return asBuilder();
184        }
185
186        public AppenderRef[] getRefs() {
187            return refs;
188        }
189
190        public B withRefs(AppenderRef[] refs) {
191            this.refs = refs;
192            return asBuilder();
193        }
194
195        public Property[] getProperties() {
196            return properties;
197        }
198
199        public B withProperties(Property[] properties) {
200            this.properties = properties;
201            return asBuilder();
202        }
203
204        public Configuration getConfig() {
205            return config;
206        }
207
208        public B withConfig(Configuration config) {
209            this.config = config;
210            return asBuilder();
211        }
212
213        public Filter getFilter() {
214            return filter;
215        }
216
217        public B withtFilter(Filter filter) {
218            this.filter = filter;
219            return asBuilder();
220        }
221
222        @Override
223        public LoggerConfig build() {
224            final String name = loggerName.equals(ROOT) ? Strings.EMPTY : loggerName;
225            LevelAndRefs container = LoggerConfig.getLevelAndRefs(level, refs, levelAndRefs, config);
226            boolean useLocation = includeLocation(includeLocation, config);
227            return new LoggerConfig(name, container.refs, filter, container.level, isAdditivity(), properties, config,
228                    useLocation);
229        }
230
231        @SuppressWarnings("unchecked")
232        public B asBuilder() {
233            return (B) this;
234        }
235    }
236
237    /**
238     * Default constructor.
239     */
240    public LoggerConfig() {
241        this.logEventFactory = LOG_EVENT_FACTORY;
242        this.level = Level.ERROR;
243        this.name = Strings.EMPTY;
244        this.properties = null;
245        this.propertiesRequireLookup = false;
246        this.config = null;
247        this.reliabilityStrategy = new DefaultReliabilityStrategy(this);
248    }
249
250    /**
251     * Constructor that sets the name, level and additive values.
252     *
253     * @param name The Logger name.
254     * @param level The Level.
255     * @param additive true if the Logger is additive, false otherwise.
256     */
257    public LoggerConfig(final String name, final Level level, final boolean additive) {
258        this.logEventFactory = LOG_EVENT_FACTORY;
259        this.name = name;
260        this.level = level;
261        this.additive = additive;
262        this.properties = null;
263        this.propertiesRequireLookup = false;
264        this.config = null;
265        this.reliabilityStrategy = new DefaultReliabilityStrategy(this);
266    }
267
268    protected LoggerConfig(final String name, final List<AppenderRef> appenders, final Filter filter,
269            final Level level, final boolean additive, final Property[] properties, final Configuration config,
270            final boolean includeLocation) {
271        super(filter);
272        this.logEventFactory = LOG_EVENT_FACTORY;
273        this.name = name;
274        this.appenderRefs = appenders;
275        this.level = level;
276        this.additive = additive;
277        this.includeLocation = includeLocation;
278        this.config = config;
279        if (properties != null && properties.length > 0) {
280            this.properties = Collections.unmodifiableList(Arrays.asList(Arrays.copyOf(
281                    properties, properties.length)));
282        } else {
283            this.properties = null;
284        }
285        this.propertiesRequireLookup = containsPropertyRequiringLookup(properties);
286        this.reliabilityStrategy = config.getReliabilityStrategy(this);
287    }
288
289    private static boolean containsPropertyRequiringLookup(final Property[] properties) {
290        if (properties == null) {
291            return false;
292        }
293        for (int i = 0; i < properties.length; i++) {
294            if (properties[i].isValueNeedsLookup()) {
295                return true;
296            }
297        }
298        return false;
299    }
300
301    @Override
302    public Filter getFilter() {
303        return super.getFilter();
304    }
305
306    /**
307     * Returns the name of the LoggerConfig.
308     *
309     * @return the name of the LoggerConfig.
310     */
311    public String getName() {
312        return name;
313    }
314
315    /**
316     * Sets the parent of this LoggerConfig.
317     *
318     * @param parent the parent LoggerConfig.
319     */
320    public void setParent(final LoggerConfig parent) {
321        this.parent = parent;
322    }
323
324    /**
325     * Returns the parent of this LoggerConfig.
326     *
327     * @return the LoggerConfig that is the parent of this one.
328     */
329    public LoggerConfig getParent() {
330        return this.parent;
331    }
332
333    /**
334     * Adds an Appender to the LoggerConfig.
335     *
336     * @param appender The Appender to add.
337     * @param level The Level to use.
338     * @param filter A Filter for the Appender reference.
339     */
340    public void addAppender(final Appender appender, final Level level, final Filter filter) {
341        appenders.add(new AppenderControl(appender, level, filter));
342    }
343
344    /**
345     * Removes the Appender with the specific name.
346     *
347     * @param name The name of the Appender.
348     */
349    public void removeAppender(final String name) {
350        AppenderControl removed = null;
351        while ((removed = appenders.remove(name)) != null) {
352            cleanupFilter(removed);
353        }
354    }
355
356    /**
357     * Returns all Appenders as a Map.
358     *
359     * @return a Map with the Appender name as the key and the Appender as the value.
360     */
361    public Map<String, Appender> getAppenders() {
362        return appenders.asMap();
363    }
364
365    /**
366     * Removes all Appenders.
367     */
368    protected void clearAppenders() {
369        do {
370            final AppenderControl[] original = appenders.clear();
371            for (final AppenderControl ctl : original) {
372                cleanupFilter(ctl);
373            }
374        } while (!appenders.isEmpty());
375    }
376
377    private void cleanupFilter(final AppenderControl ctl) {
378        final Filter filter = ctl.getFilter();
379        if (filter != null) {
380            ctl.removeFilter(filter);
381            filter.stop();
382        }
383    }
384
385    /**
386     * Returns the Appender references.
387     *
388     * @return a List of all the Appender names attached to this LoggerConfig.
389     */
390    public List<AppenderRef> getAppenderRefs() {
391        return appenderRefs;
392    }
393
394    /**
395     * Sets the logging Level.
396     *
397     * @param level The logging Level.
398     */
399    public void setLevel(final Level level) {
400        this.level = level;
401    }
402
403    /**
404     * Returns the logging Level.
405     *
406     * @return the logging Level.
407     */
408    public Level getLevel() {
409        return level == null ? parent == null ? Level.ERROR : parent.getLevel() : level;
410    }
411
412    /**
413     * Returns the LogEventFactory.
414     *
415     * @return the LogEventFactory.
416     */
417    public LogEventFactory getLogEventFactory() {
418        return logEventFactory;
419    }
420
421    /**
422     * Sets the LogEventFactory. Usually the LogEventFactory will be this LoggerConfig.
423     *
424     * @param logEventFactory the LogEventFactory.
425     */
426    public void setLogEventFactory(final LogEventFactory logEventFactory) {
427        this.logEventFactory = logEventFactory;
428    }
429
430    /**
431     * Returns the valid of the additive flag.
432     *
433     * @return true if the LoggerConfig is additive, false otherwise.
434     */
435    public boolean isAdditive() {
436        return additive;
437    }
438
439    /**
440     * Sets the additive setting.
441     *
442     * @param additive true if the LoggerConfig should be additive, false otherwise.
443     */
444    public void setAdditive(final boolean additive) {
445        this.additive = additive;
446    }
447
448    /**
449     * Returns the value of logger configuration attribute {@code includeLocation}, or, if no such attribute was
450     * configured, {@code true} if logging is synchronous or {@code false} if logging is asynchronous.
451     *
452     * @return whether location should be passed downstream
453     */
454    public boolean isIncludeLocation() {
455        return includeLocation;
456    }
457
458    /**
459     * Returns an unmodifiable map with the configuration properties, or {@code null} if this {@code LoggerConfig} does
460     * not have any configuration properties.
461     * <p>
462     * For each {@code Property} key in the map, the value is {@code true} if the property value has a variable that
463     * needs to be substituted.
464     *
465     * @return an unmodifiable map with the configuration properties, or {@code null}
466     * @see Configuration#getStrSubstitutor()
467     * @see StrSubstitutor
468     * @deprecated use {@link #getPropertyList()} instead
469     */
470    // LOG4J2-157
471    @Deprecated
472    public Map<Property, Boolean> getProperties() {
473        if (properties == null) {
474            return null;
475        }
476        if (propertiesMap == null) { // lazily initialize: only used by user custom code, not by Log4j any more
477            final Map<Property, Boolean> result = new HashMap<>(properties.size() * 2);
478            for (int i = 0; i < properties.size(); i++) {
479                result.put(properties.get(i), Boolean.valueOf(properties.get(i).isValueNeedsLookup()));
480            }
481            propertiesMap = Collections.unmodifiableMap(result);
482        }
483        return propertiesMap;
484    }
485
486    /**
487     * Returns an unmodifiable list with the configuration properties, or {@code null} if this {@code LoggerConfig} does
488     * not have any configuration properties.
489     * <p>
490     * Each {@code Property} in the list has an attribute {@link Property#isValueNeedsLookup() valueNeedsLookup} that
491     * is {@code true} if the property value has a variable that needs to be substituted.
492     *
493     * @return an unmodifiable list with the configuration properties, or {@code null}
494     * @see Configuration#getStrSubstitutor()
495     * @see StrSubstitutor
496     * @since 2.7
497     */
498    public List<Property> getPropertyList() {
499        return properties;
500    }
501
502    public boolean isPropertiesRequireLookup() {
503        return propertiesRequireLookup;
504    }
505
506    /**
507     * Logs an event.
508     *
509     * @param loggerName The name of the Logger.
510     * @param fqcn The fully qualified class name of the caller.
511     * @param marker A Marker or null if none is present.
512     * @param level The event Level.
513     * @param data The Message.
514     * @param t A Throwable or null.
515     */
516    @PerformanceSensitive("allocation")
517    public void log(final String loggerName, final String fqcn, final Marker marker, final Level level,
518            final Message data, final Throwable t) {
519        final List<Property> props = getProperties(loggerName, fqcn, marker, level, data, t);
520        final LogEvent logEvent = logEventFactory.createEvent(
521                loggerName, marker, fqcn, location(fqcn), level, data, props, t);
522        try {
523            log(logEvent, LoggerConfigPredicate.ALL);
524        } finally {
525            // LOG4J2-1583 prevent scrambled logs when logging calls are nested (logging in toString())
526            ReusableLogEventFactory.release(logEvent);
527        }
528    }
529
530    private StackTraceElement location(String fqcn) {
531        return requiresLocation() ?
532                StackLocatorUtil.calcLocation(fqcn) : null;
533    }
534
535    /**
536     * Logs an event.
537     *
538     * @param loggerName The name of the Logger.
539     * @param fqcn The fully qualified class name of the caller.
540     * @param location the location of the caller.
541     * @param marker A Marker or null if none is present.
542     * @param level The event Level.
543     * @param data The Message.
544     * @param t A Throwable or null.
545     */
546    @PerformanceSensitive("allocation")
547    public void log(final String loggerName, final String fqcn, final StackTraceElement location, final Marker marker,
548        final Level level, final Message data, final Throwable t) {
549        final List<Property> props = getProperties(loggerName, fqcn, marker, level, data, t);
550        final LogEvent logEvent = logEventFactory.createEvent(loggerName, marker, fqcn, location, level, data, props, t);
551        try {
552            log(logEvent, LoggerConfigPredicate.ALL);
553        } finally {
554            // LOG4J2-1583 prevent scrambled logs when logging calls are nested (logging in toString())
555            ReusableLogEventFactory.release(logEvent);
556        }
557    }
558
559    private List<Property> getProperties(
560            final String loggerName,
561            final String fqcn,
562            final Marker marker,
563            final Level level,
564            final Message data,
565            final Throwable t) {
566        List<Property> snapshot = properties;
567        if (snapshot == null || !propertiesRequireLookup) {
568            return snapshot;
569        }
570        return getPropertiesWithLookups(loggerName, fqcn, marker, level, data, t, snapshot);
571    }
572
573    private List<Property> getPropertiesWithLookups(
574            final String loggerName,
575            final String fqcn,
576            final Marker marker,
577            final Level level,
578            final Message data,
579            final Throwable t,
580            final List<Property> props) {
581        List<Property> results = new ArrayList<>(props.size());
582        final LogEvent event = Log4jLogEvent.newBuilder()
583                .setMessage(data)
584                .setMarker(marker)
585                .setLevel(level)
586                .setLoggerName(loggerName)
587                .setLoggerFqcn(fqcn)
588                .setThrown(t)
589                .build();
590        for (int i = 0; i < props.size(); i++) {
591            final Property prop = props.get(i);
592            final String value = prop.evaluate(config.getStrSubstitutor()); // since LOG4J2-1575
593            results.add(Property.createProperty(prop.getName(), prop.getRawValue(), value));
594        }
595        return results;
596    }
597
598    /**
599     * Logs an event.
600     *
601     * @param event The log event.
602     */
603    public void log(final LogEvent event) {
604        log(event, LoggerConfigPredicate.ALL);
605    }
606
607    /**
608     * Logs an event.
609     *
610     * @param event The log event.
611     * @param predicate predicate for which LoggerConfig instances to append to.
612     *                  A null value is equivalent to a true predicate.
613     */
614    protected void log(final LogEvent event, final LoggerConfigPredicate predicate) {
615        if (!isFiltered(event)) {
616            processLogEvent(event, predicate);
617        }
618    }
619
620    /**
621     * Returns the object responsible for ensuring log events are delivered to a working appender, even during or after
622     * a reconfiguration.
623     *
624     * @return the object responsible for delivery of log events to the appender
625     */
626    public ReliabilityStrategy getReliabilityStrategy() {
627        return reliabilityStrategy;
628    }
629
630    private void processLogEvent(final LogEvent event, final LoggerConfigPredicate predicate) {
631        event.setIncludeLocation(isIncludeLocation());
632        if (predicate.allow(this)) {
633            callAppenders(event);
634        }
635        logParent(event, predicate);
636    }
637
638    @Override
639    public boolean requiresLocation() {
640        if (!includeLocation) {
641            return false;
642        }
643        AppenderControl[] controls = appenders.get();
644        LoggerConfig loggerConfig = this;
645        while (loggerConfig != null) {
646            for (AppenderControl control : controls) {
647                Appender appender = control.getAppender();
648                if (appender instanceof LocationAware && ((LocationAware) appender).requiresLocation()) {
649                    return true;
650                }
651            }
652            if (loggerConfig.additive) {
653                loggerConfig = loggerConfig.parent;
654                if (loggerConfig != null) {
655                    controls = loggerConfig.appenders.get();
656                }
657            } else {
658                break;
659            }
660        }
661        return false;
662    }
663
664    private void logParent(final LogEvent event, final LoggerConfigPredicate predicate) {
665        if (additive && parent != null) {
666            parent.log(event, predicate);
667        }
668    }
669
670    @PerformanceSensitive("allocation")
671    protected void callAppenders(final LogEvent event) {
672        final AppenderControl[] controls = appenders.get();
673        //noinspection ForLoopReplaceableByForEach
674        for (int i = 0; i < controls.length; i++) {
675            controls[i].callAppender(event);
676        }
677    }
678
679    @Override
680    public String toString() {
681        return Strings.isEmpty(name) ? ROOT : name;
682    }
683
684    /**
685     * Factory method to create a LoggerConfig.
686     *
687     * @param additivity True if additive, false otherwise.
688     * @param level The Level to be associated with the Logger.
689     * @param loggerName The name of the Logger.
690     * @param includeLocation whether location should be passed downstream
691     * @param refs An array of Appender names.
692     * @param properties Properties to pass to the Logger.
693     * @param config The Configuration.
694     * @param filter A Filter.
695     * @return A new LoggerConfig.
696     * @deprecated Deprecated in 2.7; use {@link #createLogger(boolean, Level, String, String, AppenderRef[], Property[], Configuration, Filter)}
697     */
698    @Deprecated
699    public static LoggerConfig createLogger(final String additivity,
700            // @formatter:off
701            final Level level,
702            @PluginAttribute("name") final String loggerName,
703            final String includeLocation,
704            final AppenderRef[] refs,
705            final Property[] properties,
706            @PluginConfiguration final Configuration config,
707            final Filter filter) {
708            // @formatter:on
709        if (loggerName == null) {
710            LOGGER.error("Loggers cannot be configured without a name");
711            return null;
712        }
713
714        final List<AppenderRef> appenderRefs = Arrays.asList(refs);
715        final String name = loggerName.equals(ROOT) ? Strings.EMPTY : loggerName;
716        final boolean additive = Booleans.parseBoolean(additivity, true);
717
718        return new LoggerConfig(name, appenderRefs, filter, level, additive, properties, config,
719                includeLocation(includeLocation, config));
720    }
721
722    /**
723     * Factory method to create a LoggerConfig.
724     *
725     * @param additivity true if additive, false otherwise.
726     * @param level The Level to be associated with the Logger.
727     * @param loggerName The name of the Logger.
728     * @param includeLocation whether location should be passed downstream
729     * @param refs An array of Appender names.
730     * @param properties Properties to pass to the Logger.
731     * @param config The Configuration.
732     * @param filter A Filter.
733     * @return A new LoggerConfig.
734     * @since 2.6
735     */
736    @Deprecated
737    public static LoggerConfig createLogger(
738            // @formatter:off
739            @PluginAttribute(value = "additivity", defaultBoolean = true) final boolean additivity,
740            @PluginAttribute("level") final Level level,
741            @Required(message = "Loggers cannot be configured without a name") @PluginAttribute("name") final String loggerName,
742            @PluginAttribute("includeLocation") final String includeLocation,
743            @PluginElement("AppenderRef") final AppenderRef[] refs,
744            @PluginElement("Properties") final Property[] properties,
745            @PluginConfiguration final Configuration config,
746            @PluginElement("Filter") final Filter filter
747            // @formatter:on
748    ) {
749        final String name = loggerName.equals(ROOT) ? Strings.EMPTY : loggerName;
750        return new LoggerConfig(name, Arrays.asList(refs), filter, level, additivity, properties, config,
751                includeLocation(includeLocation, config));
752    }
753
754    /**
755     */
756    protected static boolean includeLocation(final String includeLocationConfigValue) {
757        return includeLocation(includeLocationConfigValue, null);
758    }
759
760    // Note: for asynchronous loggers, includeLocation default is FALSE,
761    // for synchronous loggers, includeLocation default is TRUE.
762    protected static boolean includeLocation(final String includeLocationConfigValue, final Configuration configuration) {
763        if (includeLocationConfigValue == null) {
764            LoggerContext context = null;
765            if (configuration != null) {
766                context = configuration.getLoggerContext();
767            }
768            if (context != null) {
769                return !(context instanceof AsyncLoggerContext);
770            } else {
771                return !AsyncLoggerContextSelector.isSelected();
772            }
773        }
774        return Boolean.parseBoolean(includeLocationConfigValue);
775    }
776
777    protected final boolean hasAppenders() {
778        return !appenders.isEmpty();
779    }
780
781    /**
782     * The root Logger.
783     */
784    @Plugin(name = ROOT, category = Core.CATEGORY_NAME, printObject = true)
785    public static class RootLogger extends LoggerConfig {
786
787        @PluginBuilderFactory
788        public static <B extends Builder<B>> B newRootBuilder() {
789            return new Builder<B>().asBuilder();
790        }
791
792        /**
793         * Builds LoggerConfig instances.
794         *
795         * @param <B>
796         *            The type to build
797         */
798        public static class Builder<B extends Builder<B>>
799                implements org.apache.logging.log4j.core.util.Builder<LoggerConfig> {
800
801            @PluginBuilderAttribute
802            private boolean additivity;
803            @PluginBuilderAttribute
804            private Level level;
805            @PluginBuilderAttribute
806            private String levelAndRefs;
807            @PluginBuilderAttribute
808            private String includeLocation;
809            @PluginElement("AppenderRef")
810            private AppenderRef[] refs;
811            @PluginElement("Properties")
812            private Property[] properties;
813            @PluginConfiguration
814            private Configuration config;
815            @PluginElement("Filter")
816            private Filter filter;
817
818            public boolean isAdditivity() {
819                return additivity;
820            }
821
822            public B withAdditivity(boolean additivity) {
823                this.additivity = additivity;
824                return asBuilder();
825            }
826
827            public Level getLevel() {
828                return level;
829            }
830
831            public B withLevel(Level level) {
832                this.level = level;
833                return asBuilder();
834            }
835
836            public String getLevelAndRefs() {
837                return levelAndRefs;
838            }
839
840            public B withLevelAndRefs(String levelAndRefs) {
841                this.levelAndRefs = levelAndRefs;
842                return asBuilder();
843            }
844
845            public String getIncludeLocation() {
846                return includeLocation;
847            }
848
849            public B withIncludeLocation(String includeLocation) {
850                this.includeLocation = includeLocation;
851                return asBuilder();
852            }
853
854            public AppenderRef[] getRefs() {
855                return refs;
856            }
857
858            public B withRefs(AppenderRef[] refs) {
859                this.refs = refs;
860                return asBuilder();
861            }
862
863            public Property[] getProperties() {
864                return properties;
865            }
866
867            public B withProperties(Property[] properties) {
868                this.properties = properties;
869                return asBuilder();
870            }
871
872            public Configuration getConfig() {
873                return config;
874            }
875
876            public B withConfig(Configuration config) {
877                this.config = config;
878                return asBuilder();
879            }
880
881            public Filter getFilter() {
882                return filter;
883            }
884
885            public B withtFilter(Filter filter) {
886                this.filter = filter;
887                return asBuilder();
888            }
889
890            @Override
891            public LoggerConfig build() {
892                LevelAndRefs container = LoggerConfig.getLevelAndRefs(level, refs, levelAndRefs, config);
893                return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, container.refs, filter, container.level,
894                        additivity, properties, config, includeLocation(includeLocation, config));
895            }
896
897            @SuppressWarnings("unchecked")
898            public B asBuilder() {
899                return (B) this;
900            }
901        }
902
903
904        @Deprecated
905        public static LoggerConfig createLogger(
906                // @formatter:off
907                @PluginAttribute("additivity") final String additivity,
908                @PluginAttribute("level") final Level level,
909                @PluginAttribute("includeLocation") final String includeLocation,
910                @PluginElement("AppenderRef") final AppenderRef[] refs,
911                @PluginElement("Properties") final Property[] properties,
912                @PluginConfiguration final Configuration config,
913                @PluginElement("Filter") final Filter filter) {
914                // @formatter:on
915            final List<AppenderRef> appenderRefs = Arrays.asList(refs);
916            final Level actualLevel = level == null ? Level.ERROR : level;
917            final boolean additive = Booleans.parseBoolean(additivity, true);
918            return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs, filter, actualLevel, additive,
919                    properties, config, includeLocation(includeLocation, config));
920        }
921    }
922
923    protected static LevelAndRefs getLevelAndRefs(Level level, AppenderRef[] refs, String levelAndRefs,
924            Configuration config) {
925        LevelAndRefs result = new LevelAndRefs();
926        if (levelAndRefs != null) {
927            if (config instanceof PropertiesConfiguration) {
928                if (level != null) {
929                    LOGGER.warn("Level is ignored when levelAndRefs syntax is used.");
930                }
931                if (refs != null && refs.length > 0) {
932                    LOGGER.warn("Appender references are ignored when levelAndRefs syntax is used");
933                }
934                final String[] parts = Strings.splitList(levelAndRefs);
935                result.level = Level.getLevel(parts[0]);
936                if (parts.length > 1) {
937                    List<AppenderRef> refList = new ArrayList<>();
938                    Arrays.stream(parts).skip(1).forEach((ref) ->
939                            refList.add(AppenderRef.createAppenderRef(ref, null, null)));
940                    result.refs = refList;
941                }
942            } else {
943                LOGGER.warn("levelAndRefs are only allowed in a properties configuration. The value is ignored.");
944                result.level = level;
945                result.refs = Arrays.asList(refs);
946            }
947        } else {
948            result.level = level;
949            result.refs = Arrays.asList(refs);
950        }
951        return result;
952    }
953
954    protected static class LevelAndRefs {
955        public Level level;
956        public List<AppenderRef> refs;
957    }
958
959    protected enum LoggerConfigPredicate {
960        ALL() {
961            @Override
962            boolean allow(final LoggerConfig config) {
963                return true;
964            }
965        },
966        ASYNCHRONOUS_ONLY() {
967            @Override
968            boolean allow(final LoggerConfig config) {
969                return config instanceof AsyncLoggerConfig;
970            }
971        },
972        SYNCHRONOUS_ONLY() {
973            @Override
974            boolean allow(final LoggerConfig config) {
975                return !ASYNCHRONOUS_ONLY.allow(config);
976            }
977        };
978
979        abstract boolean allow(LoggerConfig config);
980    }
981}