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.async;
018
019import org.apache.logging.log4j.Level;
020import org.apache.logging.log4j.Logger;
021import org.apache.logging.log4j.core.util.Loader;
022import org.apache.logging.log4j.status.StatusLogger;
023import org.apache.logging.log4j.util.PropertiesUtil;
024
025/**
026 * Creates {@link AsyncQueueFullPolicy} instances based on user-specified system properties. The {@code AsyncQueueFullPolicy}
027 * created by this factory is used in AsyncLogger, AsyncLoggerConfig and AsyncAppender
028 * to control if events are logged in the current thread, the background thread, or discarded.
029 * <p>
030 * Property {@code "log4j2.AsyncQueueFullPolicy"} controls the routing behaviour. If this property is not specified or has
031 * value {@code "Default"}, this factory creates {@link DefaultAsyncQueueFullPolicy} objects.
032 * </p> <p>
033 * If this property has value {@code "Discard"}, this factory creates {@link DiscardingAsyncQueueFullPolicy} objects.
034 * By default, this router discards events of level {@code INFO}, {@code DEBUG} and {@code TRACE} if the queue is full.
035 * This can be adjusted with property {@code "log4j2.DiscardThreshold"} (name of the level at which to start
036 * discarding).
037 * </p> <p>
038 * For any other value, this
039 * factory interprets the value as the fully qualified name of a class implementing the {@link AsyncQueueFullPolicy}
040 * interface. The class must have a default constructor.
041 * </p>
042 *
043 * @since 2.6
044 */
045public class AsyncQueueFullPolicyFactory {
046    static final String PROPERTY_NAME_ASYNC_EVENT_ROUTER = "log4j2.AsyncQueueFullPolicy";
047    static final String PROPERTY_VALUE_DEFAULT_ASYNC_EVENT_ROUTER = "Default";
048    static final String PROPERTY_VALUE_DISCARDING_ASYNC_EVENT_ROUTER = "Discard";
049    static final String PROPERTY_NAME_DISCARDING_THRESHOLD_LEVEL = "log4j2.DiscardThreshold";
050
051    private static final Logger LOGGER = StatusLogger.getLogger();
052
053    /**
054     * Creates and returns {@link AsyncQueueFullPolicy} instances based on user-specified system properties.
055     * <p>
056     * Property {@code "log4j2.AsyncQueueFullPolicy"} controls the routing behaviour. If this property is not specified or
057     * has value {@code "Default"}, this method returns {@link DefaultAsyncQueueFullPolicy} objects.
058     * </p> <p>
059     * If this property has value {@code "Discard"}, this method returns {@link DiscardingAsyncQueueFullPolicy} objects.
060     * </p> <p>
061     * For any other value, this method interprets the value as the fully qualified name of a class implementing the
062     * {@link AsyncQueueFullPolicy} interface. The class must have a default constructor.
063     * </p>
064     *
065     * @return a new AsyncQueueFullPolicy
066     */
067    public static AsyncQueueFullPolicy create() {
068        final String router = PropertiesUtil.getProperties().getStringProperty(PROPERTY_NAME_ASYNC_EVENT_ROUTER);
069        if (router == null || isRouterSelected(
070                router, DefaultAsyncQueueFullPolicy.class, PROPERTY_VALUE_DEFAULT_ASYNC_EVENT_ROUTER)) {
071            return new DefaultAsyncQueueFullPolicy();
072        }
073        if (isRouterSelected(
074                router, DiscardingAsyncQueueFullPolicy.class, PROPERTY_VALUE_DISCARDING_ASYNC_EVENT_ROUTER)) {
075            return createDiscardingAsyncQueueFullPolicy();
076        }
077        return createCustomRouter(router);
078    }
079
080    private static boolean isRouterSelected(
081            String propertyValue,
082            Class<? extends AsyncQueueFullPolicy> policy,
083            String shortPropertyValue) {
084        return propertyValue != null && (shortPropertyValue.equalsIgnoreCase(propertyValue)
085                || policy.getName().equals(propertyValue)
086                || policy.getSimpleName().equals(propertyValue));
087    }
088
089    private static AsyncQueueFullPolicy createCustomRouter(final String router) {
090        try {
091            final Class<? extends AsyncQueueFullPolicy> cls = Loader.loadClass(router).asSubclass(AsyncQueueFullPolicy.class);
092            LOGGER.debug("Creating custom AsyncQueueFullPolicy '{}'", router);
093            return cls.newInstance();
094        } catch (final Exception ex) {
095            LOGGER.debug("Using DefaultAsyncQueueFullPolicy. Could not create custom AsyncQueueFullPolicy '{}': {}", router,
096                    ex.toString());
097            return new DefaultAsyncQueueFullPolicy();
098        }
099    }
100
101    private static AsyncQueueFullPolicy createDiscardingAsyncQueueFullPolicy() {
102        final PropertiesUtil util = PropertiesUtil.getProperties();
103        final String level = util.getStringProperty(PROPERTY_NAME_DISCARDING_THRESHOLD_LEVEL, Level.INFO.name());
104        final Level thresholdLevel = Level.toLevel(level, Level.INFO);
105        LOGGER.debug("Creating custom DiscardingAsyncQueueFullPolicy(discardThreshold:{})", thresholdLevel);
106        return new DiscardingAsyncQueueFullPolicy(thresholdLevel);
107    }
108}