    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     */
    017    package org.apache.logging.log4j.core.appender.routing;
    018
    019    import java.util.Map;
    020    import java.util.concurrent.ConcurrentHashMap;
    021    import java.util.concurrent.ConcurrentMap;
    022
    023    import org.apache.logging.log4j.core.Appender;
    024    import org.apache.logging.log4j.core.Filter;
    025    import org.apache.logging.log4j.core.LogEvent;
    026    import org.apache.logging.log4j.core.appender.AbstractAppender;
    027    import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
    028    import org.apache.logging.log4j.core.config.AppenderControl;
    029    import org.apache.logging.log4j.core.config.Configuration;
    030    import org.apache.logging.log4j.core.config.Node;
    031    import org.apache.logging.log4j.core.config.plugins.Plugin;
    032    import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
    033    import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
    034    import org.apache.logging.log4j.core.config.plugins.PluginElement;
    035    import org.apache.logging.log4j.core.config.plugins.PluginFactory;
    036    import org.apache.logging.log4j.core.util.Booleans;
    037
    038    /**
    039     * This Appender "routes" between various Appenders, some of which can be references to
    040     * Appenders defined earlier in the configuration while others can be dynamically created
    041     * within this Appender as required. Routing is achieved by specifying a pattern on
    042     * the Routing appender declaration. The pattern should contain one or more substitution patterns of
    043     * the form "$${[key:]token}". The pattern will be resolved each time the Appender is called using
    044     * the built in StrSubstitutor and the StrLookup plugin that matches the specified key.
    045     */
    046    @Plugin(name = "Routing", category = "Core", elementType = "appender", printObject = true)
    047    public final class RoutingAppender extends AbstractAppender {
    048        private static final long serialVersionUID = 1L;
    049        private static final String DEFAULT_KEY = "ROUTING_APPENDER_DEFAULT";
    050        private final Routes routes;
    051        private final Route defaultRoute;
    052        private final Configuration config;
    053        private final ConcurrentMap<String, AppenderControl> appenders =
    054                new ConcurrentHashMap<String, AppenderControl>();
    055        private final RewritePolicy rewritePolicy;
    056
    057        private RoutingAppender(final String name, final Filter filter, final boolean ignoreExceptions, final Routes routes,
    058                                final RewritePolicy rewritePolicy, final Configuration config) {
    059            super(name, filter, null, ignoreExceptions);
    060            this.routes = routes;
    061            this.config = config;
    062            this.rewritePolicy = rewritePolicy;
    063            Route defRoute = null;
    064            for (final Route route : routes.getRoutes()) {
    065                if (route.getKey() == null) {
    066                    if (defRoute == null) {
    067                        defRoute = route;
    068                    } else {
    069                        error("Multiple default routes. Route " + route.toString() + " will be ignored");
    070                    }
    071                }
    072            }
    073            defaultRoute = defRoute;
    074        }
    075
    076        @Override
    077        public void start() {
    078            // Register all the static routes.
    079            for (final Route route : routes.getRoutes()) {
    080                if (route.getAppenderRef() != null) {
    081                    final Appender appender = config.getAppender(route.getAppenderRef());
    082                    if (appender != null) {
    083                        final String key = route == defaultRoute ? DEFAULT_KEY : route.getKey();
    084                        appenders.put(key, new AppenderControl(appender, null, null));
    085                    } else {
    086                        LOGGER.error("Appender " + route.getAppenderRef() + " cannot be located. Route ignored");
    087                    }
    088                }
    089            }
    090            super.start();
    091        }
    092
    093        @Override
    094        public void stop() {
    095            super.stop();
    096            final Map<String, Appender> map = config.getAppenders();
    097            for (final Map.Entry<String, AppenderControl> entry : appenders.entrySet()) {
    098                final String name = entry.getValue().getAppender().getName();
    099                if (!map.containsKey(name)) {
    100                    entry.getValue().getAppender().stop();
    101                }
    102            }
    103        }
    104
    105        @Override
    106        public void append(LogEvent event) {
    107            if (rewritePolicy != null) {
    108                event = rewritePolicy.rewrite(event);
    109            }
    110            final String key = config.getStrSubstitutor().replace(event, routes.getPattern());
    111            final AppenderControl control = getControl(key, event);
    112            if (control != null) {
    113                control.callAppender(event);
    114            }
    115        }
    116
    117        private synchronized AppenderControl getControl(final String key, final LogEvent event) {
    118            AppenderControl control = appenders.get(key);
    119            if (control != null) {
    120                return control;
    121            }
    122            Route route = null;
    123            for (final Route r : routes.getRoutes()) {
    124                if (r.getAppenderRef() == null && key.equals(r.getKey())) {
    125                    route = r;
    126                    break;
    127                }
    128            }
    129            if (route == null) {
    130                route = defaultRoute;
    131                control = appenders.get(DEFAULT_KEY);
    132                if (control != null) {
    133                    return control;
    134                }
    135            }
    136            if (route != null) {
    137                final Appender app = createAppender(route, event);
    138                if (app == null) {
    139                    return null;
    140                }
    141                control = new AppenderControl(app, null, null);
    142                appenders.put(key, control);
    143            }
    144
    145            return control;
    146        }
    147
    148        private Appender createAppender(final Route route, final LogEvent event) {
    149            final Node routeNode = route.getNode();
    150            for (final Node node : routeNode.getChildren()) {
    151                if (node.getType().getElementName().equals("appender")) {
    152                    final Node appNode = new Node(node);
    153                    config.createConfiguration(appNode, event);
    154                    if (appNode.getObject() instanceof Appender) {
    155                        final Appender app = (Appender) appNode.getObject();
    156                        app.start();
    157                        return app;
    158                    }
    159                    LOGGER.error("Unable to create Appender of type " + node.getName());
    160                    return null;
    161                }
    162            }
    163            LOGGER.error("No Appender was configured for route " + route.getKey());
    164            return null;
    165        }
    166
    167        /**
    168         * Create a RoutingAppender.
    169         * @param name The name of the Appender.
    170         * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
    171         *               they are propagated to the caller.
    172         * @param routes The routing definitions.
    173         * @param config The Configuration (automatically added by the Configuration).
    174         * @param rewritePolicy A RewritePolicy, if any.
    175         * @param filter A Filter to restrict events processed by the Appender or null.
    176         * @return The RoutingAppender
    177         */
    178        @PluginFactory
    179        public static RoutingAppender createAppender(
    180                @PluginAttribute("name") final String name,
    181                @PluginAttribute("ignoreExceptions") final String ignore,
    182                @PluginElement("Routes") final Routes routes,
    183                @PluginConfiguration final Configuration config,
    184                @PluginElement("RewritePolicy") final RewritePolicy rewritePolicy,
    185                @PluginElement("Filter") final Filter filter) {
    186
    187            final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
    188            if (name == null) {
    189                LOGGER.error("No name provided for RoutingAppender");
    190                return null;
    191            }
    192            if (routes == null) {
    193                LOGGER.error("No routes defined for RoutingAppender");
    194                return null;
    195            }
    196            return new RoutingAppender(name, filter, ignoreExceptions, routes, rewritePolicy, config);
    197        }
    198    }