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.appender.routing; 018 019import java.util.Map; 020import java.util.concurrent.ConcurrentHashMap; 021import java.util.concurrent.ConcurrentMap; 022 023import org.apache.logging.log4j.core.Appender; 024import org.apache.logging.log4j.core.Filter; 025import org.apache.logging.log4j.core.LogEvent; 026import org.apache.logging.log4j.core.appender.AbstractAppender; 027import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy; 028import org.apache.logging.log4j.core.config.AppenderControl; 029import org.apache.logging.log4j.core.config.Configuration; 030import org.apache.logging.log4j.core.config.Node; 031import org.apache.logging.log4j.core.config.plugins.Plugin; 032import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 033import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 034import org.apache.logging.log4j.core.config.plugins.PluginElement; 035import org.apache.logging.log4j.core.config.plugins.PluginFactory; 036import 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) 047public 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}