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.appender.routing;
    18
    19  import java.util.Map;
    20  import java.util.concurrent.ConcurrentHashMap;
    21  import java.util.concurrent.ConcurrentMap;
    22
    23  import org.apache.logging.log4j.core.Appender;
    24  import org.apache.logging.log4j.core.Filter;
    25  import org.apache.logging.log4j.core.LogEvent;
    26  import org.apache.logging.log4j.core.appender.AbstractAppender;
    27  import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
    28  import org.apache.logging.log4j.core.config.AppenderControl;
    29  import org.apache.logging.log4j.core.config.Configuration;
    30  import org.apache.logging.log4j.core.config.Node;
    31  import org.apache.logging.log4j.core.config.plugins.Plugin;
    32  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
    33  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
    34  import org.apache.logging.log4j.core.config.plugins.PluginElement;
    35  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
    36  import org.apache.logging.log4j.core.util.Booleans;
    37
    38  /**
    39   * This Appender "routes" between various Appenders, some of which can be references to
    40   * Appenders defined earlier in the configuration while others can be dynamically created
    41   * within this Appender as required. Routing is achieved by specifying a pattern on
    42   * the Routing appender declaration. The pattern should contain one or more substitution patterns of
    43   * the form "$${[key:]token}". The pattern will be resolved each time the Appender is called using
    44   * the built in StrSubstitutor and the StrLookup plugin that matches the specified key.
    45   */
    46  @Plugin(name = "Routing", category = "Core", elementType = "appender", printObject = true)
    47  public final class RoutingAppender extends AbstractAppender {
    48      private static final long serialVersionUID = 1L;
    49      private static final String DEFAULT_KEY = "ROUTING_APPENDER_DEFAULT";
    50      private final Routes routes;
    51      private final Route defaultRoute;
    52      private final Configuration config;
    53      private final ConcurrentMap<String, AppenderControl> appenders =
    54              new ConcurrentHashMap<String, AppenderControl>();
    55      private final RewritePolicy rewritePolicy;
    56
    57      private RoutingAppender(final String name, final Filter filter, final boolean ignoreExceptions, final Routes routes,
    58                              final RewritePolicy rewritePolicy, final Configuration config) {
    59          super(name, filter, null, ignoreExceptions);
    60          this.routes = routes;
    61          this.config = config;
    62          this.rewritePolicy = rewritePolicy;
    63          Route defRoute = null;
    64          for (final Route route : routes.getRoutes()) {
    65              if (route.getKey() == null) {
    66                  if (defRoute == null) {
    67                      defRoute = route;
    68                  } else {
    69                      error("Multiple default routes. Route " + route.toString() + " will be ignored");
    70                  }
    71              }
    72          }
    73          defaultRoute = defRoute;
    74      }
    75
    76      @Override
    77      public void start() {
    78          // Register all the static routes.
    79          for (final Route route : routes.getRoutes()) {
    80              if (route.getAppenderRef() != null) {
    81                  final Appender appender = config.getAppender(route.getAppenderRef());
    82                  if (appender != null) {
    83                      final String key = route == defaultRoute ? DEFAULT_KEY : route.getKey();
    84                      appenders.put(key, new AppenderControl(appender, null, null));
    85                  } else {
    86                      LOGGER.error("Appender " + route.getAppenderRef() + " cannot be located. Route ignored");
    87                  }
    88              }
    89          }
    90          super.start();
    91      }
    92
    93      @Override
    94      public void stop() {
    95          super.stop();
    96          final Map<String, Appender> map = config.getAppenders();
    97          for (final Map.Entry<String, AppenderControl> entry : appenders.entrySet()) {
    98              final String name = entry.getValue().getAppender().getName();
    99              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 }