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 String DEFAULT_KEY = "ROUTING_APPENDER_DEFAULT";
49      private final Routes routes;
50      private final Route defaultRoute;
51      private final Configuration config;
52      private final ConcurrentMap<String, AppenderControl> appenders =
53              new ConcurrentHashMap<String, AppenderControl>();
54      private final RewritePolicy rewritePolicy;
55  
56      private RoutingAppender(final String name, final Filter filter, final boolean ignoreExceptions, final Routes routes,
57                              final RewritePolicy rewritePolicy, final Configuration config) {
58          super(name, filter, null, ignoreExceptions);
59          this.routes = routes;
60          this.config = config;
61          this.rewritePolicy = rewritePolicy;
62          Route defRoute = null;
63          for (final Route route : routes.getRoutes()) {
64              if (route.getKey() == null) {
65                  if (defRoute == null) {
66                      defRoute = route;
67                  } else {
68                      error("Multiple default routes. Route " + route.toString() + " will be ignored");
69                  }
70              }
71          }
72          defaultRoute = defRoute;
73      }
74  
75      @Override
76      public void start() {
77          // Register all the static routes.
78          for (final Route route : routes.getRoutes()) {
79              if (route.getAppenderRef() != null) {
80                  final Appender appender = config.getAppender(route.getAppenderRef());
81                  if (appender != null) {
82                      final String key = route == defaultRoute ? DEFAULT_KEY : route.getKey();
83                      appenders.put(key, new AppenderControl(appender, null, null));
84                  } else {
85                      LOGGER.error("Appender " + route.getAppenderRef() + " cannot be located. Route ignored");
86                  }
87              }
88          }
89          super.start();
90      }
91  
92      @Override
93      public void stop() {
94          super.stop();
95          final Map<String, Appender> map = config.getAppenders();
96          for (final Map.Entry<String, AppenderControl> entry : appenders.entrySet()) {
97              final String name = entry.getValue().getAppender().getName();
98              if (!map.containsKey(name)) {
99                  entry.getValue().getAppender().stop();
100             }
101         }
102     }
103 
104     @Override
105     public void append(LogEvent event) {
106         if (rewritePolicy != null) {
107             event = rewritePolicy.rewrite(event);
108         }
109         final String key = config.getStrSubstitutor().replace(event, routes.getPattern());
110         final AppenderControl control = getControl(key, event);
111         if (control != null) {
112             control.callAppender(event);
113         }
114     }
115 
116     private synchronized AppenderControl getControl(final String key, final LogEvent event) {
117         AppenderControl control = appenders.get(key);
118         if (control != null) {
119             return control;
120         }
121         Route route = null;
122         for (final Route r : routes.getRoutes()) {
123             if (r.getAppenderRef() == null && key.equals(r.getKey())) {
124                 route = r;
125                 break;
126             }
127         }
128         if (route == null) {
129             route = defaultRoute;
130             control = appenders.get(DEFAULT_KEY);
131             if (control != null) {
132                 return control;
133             }
134         }
135         if (route != null) {
136             final Appender app = createAppender(route, event);
137             if (app == null) {
138                 return null;
139             }
140             control = new AppenderControl(app, null, null);
141             appenders.put(key, control);
142         }
143 
144         return control;
145     }
146 
147     private Appender createAppender(final Route route, final LogEvent event) {
148         final Node routeNode = route.getNode();
149         for (final Node node : routeNode.getChildren()) {
150             if (node.getType().getElementName().equals("appender")) {
151                 final Node appNode = new Node(node);
152                 config.createConfiguration(appNode, event);
153                 if (appNode.getObject() instanceof Appender) {
154                     final Appender app = (Appender) appNode.getObject();
155                     app.start();
156                     return app;
157                 }
158                 LOGGER.error("Unable to create Appender of type " + node.getName());
159                 return null;
160             }
161         }
162         LOGGER.error("No Appender was configured for route " + route.getKey());
163         return null;
164     }
165 
166     /**
167      * Create a RoutingAppender.
168      * @param name The name of the Appender.
169      * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
170      *               they are propagated to the caller.
171      * @param routes The routing definitions.
172      * @param config The Configuration (automatically added by the Configuration).
173      * @param rewritePolicy A RewritePolicy, if any.
174      * @param filter A Filter to restrict events processed by the Appender or null.
175      * @return The RoutingAppender
176      */
177     @PluginFactory
178     public static RoutingAppender createAppender(
179             @PluginAttribute("name") final String name,
180             @PluginAttribute("ignoreExceptions") final String ignore,
181             @PluginElement("Routes") final Routes routes,
182             @PluginConfiguration final Configuration config,
183             @PluginElement("RewritePolicy") final RewritePolicy rewritePolicy,
184             @PluginElement("Filter") final Filter filter) {
185 
186         final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
187         if (name == null) {
188             LOGGER.error("No name provided for RoutingAppender");
189             return null;
190         }
191         if (routes == null) {
192             LOGGER.error("No routes defined for RoutingAppender");
193             return null;
194         }
195         return new RoutingAppender(name, filter, ignoreExceptions, routes, rewritePolicy, config);
196     }
197 }