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 org.apache.logging.log4j.core.Appender;
20  import org.apache.logging.log4j.core.Filter;
21  import org.apache.logging.log4j.core.LogEvent;
22  import org.apache.logging.log4j.core.appender.AbstractAppender;
23  import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
24  import org.apache.logging.log4j.core.config.AppenderControl;
25  import org.apache.logging.log4j.core.config.Configuration;
26  import org.apache.logging.log4j.core.config.Node;
27  import org.apache.logging.log4j.core.config.plugins.Plugin;
28  import org.apache.logging.log4j.core.config.plugins.PluginAttr;
29  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
30  import org.apache.logging.log4j.core.config.plugins.PluginElement;
31  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
32  
33  import java.util.Map;
34  import java.util.concurrent.ConcurrentHashMap;
35  import java.util.concurrent.ConcurrentMap;
36  
37  /**
38   * This Appender "routes" between various Appenders, some of which can be references to
39   * Appenders defined earlier in the configuration while others can be dynamically created
40   * within this Appender as required. Routing is achieved by specifying a pattern on
41   * the Routing appender declaration. The pattern should contain one or more substitution patterns of
42   * the form "$${[key:]token}". The pattern will be resolved each time the Appender is called using
43   * the built in StrSubstitutor and the StrLookup plugin that matches the specified key.
44   */
45  @Plugin(name = "Routing", type = "Core", elementType = "appender", printObject = true)
46  public final class RoutingAppender extends AbstractAppender {
47      private static final String DEFAULT_KEY = "ROUTING_APPENDER_DEFAULT";
48      private final Routes routes;
49      private final Route defaultRoute;
50      private final Configuration config;
51      private final ConcurrentMap<String, AppenderControl> appenders = new ConcurrentHashMap<String, AppenderControl>();
52      private final RewritePolicy rewritePolicy;
53  
54      private RoutingAppender(final String name, final Filter filter, final boolean handleException, final Routes routes,
55                              final RewritePolicy rewritePolicy, final Configuration config) {
56          super(name, filter, null, handleException);
57          this.routes = routes;
58          this.config = config;
59          this.rewritePolicy = rewritePolicy;
60          Route defRoute = null;
61          for (final Route route : routes.getRoutes()) {
62              if (route.getKey() == null) {
63                  if (defRoute == null) {
64                      defRoute = route;
65                  } else {
66                      error("Multiple default routes. Route " + route.toString() + " will be ignored");
67                  }
68              }
69          }
70          defaultRoute = defRoute;
71      }
72  
73      @Override
74      public void start() {
75          final Map<String, Appender<?>> map = config.getAppenders();
76          // Register all the static routes.
77          for (final Route route : routes.getRoutes()) {
78              if (route.getAppenderRef() != null) {
79                  final Appender appender = map.get(route.getAppenderRef());
80                  if (appender != null) {
81                      final String key = route == defaultRoute ? DEFAULT_KEY : route.getKey();
82                      appenders.put(key, new AppenderControl(appender, null, null));
83                  } else {
84                      LOGGER.error("Appender " + route.getAppenderRef() + " cannot be located. Route ignored");
85                  }
86              }
87          }
88          super.start();
89      }
90  
91      @Override
92      public void stop() {
93          super.stop();
94          final Map<String, Appender<?>> map = config.getAppenders();
95          for (final Map.Entry<String, AppenderControl> entry : appenders.entrySet()) {
96              final String name = entry.getValue().getAppender().getName();
97              if (!map.containsKey(name)) {
98                  entry.getValue().getAppender().stop();
99              }
100         }
101     }
102 
103     public void append(LogEvent event) {
104         if (rewritePolicy != null) {
105             event = rewritePolicy.rewrite(event);
106         }
107         final String key = config.getSubst().replace(event, routes.getPattern());
108         final AppenderControl control = getControl(key, event);
109         if (control != null) {
110             control.callAppender(event);
111         }
112     }
113 
114     private synchronized AppenderControl getControl(final String key, final LogEvent event) {
115         AppenderControl control = appenders.get(key);
116         if (control != null) {
117             return control;
118         }
119         Route route = null;
120         for (final Route r : routes.getRoutes()) {
121             if (r.getAppenderRef() == null && key.equals(r.getKey())) {
122                 route = r;
123                 break;
124             }
125         }
126         if (route == null) {
127             route = defaultRoute;
128         }
129         if (route != null) {
130             final Appender app = createAppender(route, event);
131             if (app == null) {
132                 return null;
133             }
134             control = new AppenderControl(app, null, null);
135             appenders.put(key, control);
136         }
137 
138         return control;
139     }
140 
141     private Appender createAppender(final Route route, final LogEvent event) {
142         final Node routeNode = route.getNode();
143         for (final Node node : routeNode.getChildren()) {
144             if (node.getType().getElementName().equals("appender")) {
145                 final Node appNode = new Node(node);
146                 config.createConfiguration(appNode, event);
147                 if (appNode.getObject() instanceof Appender) {
148                     final Appender app = (Appender) appNode.getObject();
149                     app.start();
150                     return app;
151                 }
152                 LOGGER.error("Unable to create Appender of type " + node.getName());
153                 return null;
154             }
155         }
156         LOGGER.error("No Appender was configured for route " + route.getKey());
157         return null;
158     }
159 
160     /**
161      * Create a RoutingAppender.
162      * @param name The name of the Appender.
163      * @param suppress "true" if exceptions should be hidden from the application, "false" otherwise.
164      * The default is "true".
165      * @param routes The routing definitions.
166      * @param config The Configuration (automatically added by the Configuration).
167      * @param rewritePolicy A RewritePolicy, if any.
168      * @param filter A Filter to restrict events processed by the Appender or null.
169      * @return The RoutingAppender
170      */
171     @PluginFactory
172     public static RoutingAppender createAppender(@PluginAttr("name") final String name,
173                                           @PluginAttr("suppressExceptions") final String suppress,
174                                           @PluginElement("routes") final Routes routes,
175                                           @PluginConfiguration final Configuration config,
176                                           @PluginElement("rewritePolicy") final RewritePolicy rewritePolicy,
177                                           @PluginElement("filters") final Filter filter) {
178 
179         final boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress);
180 
181         if (name == null) {
182             LOGGER.error("No name provided for RoutingAppender");
183             return null;
184         }
185         if (routes == null) {
186             LOGGER.error("No routes defined for RoutingAppender");
187             return null;
188         }
189         return new RoutingAppender(name, filter, handleExceptions, routes, rewritePolicy, config);
190     }
191 }