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 String DEFAULT_KEY = "ROUTING_APPENDER_DEFAULT"; 049 private final Routes routes; 050 private final Route defaultRoute; 051 private final Configuration config; 052 private final ConcurrentMap<String, AppenderControl> appenders = 053 new ConcurrentHashMap<String, AppenderControl>(); 054 private final RewritePolicy rewritePolicy; 055 056 private RoutingAppender(final String name, final Filter filter, final boolean ignoreExceptions, final Routes routes, 057 final RewritePolicy rewritePolicy, final Configuration config) { 058 super(name, filter, null, ignoreExceptions); 059 this.routes = routes; 060 this.config = config; 061 this.rewritePolicy = rewritePolicy; 062 Route defRoute = null; 063 for (final Route route : routes.getRoutes()) { 064 if (route.getKey() == null) { 065 if (defRoute == null) { 066 defRoute = route; 067 } else { 068 error("Multiple default routes. Route " + route.toString() + " will be ignored"); 069 } 070 } 071 } 072 defaultRoute = defRoute; 073 } 074 075 @Override 076 public void start() { 077 // Register all the static routes. 078 for (final Route route : routes.getRoutes()) { 079 if (route.getAppenderRef() != null) { 080 final Appender appender = config.getAppender(route.getAppenderRef()); 081 if (appender != null) { 082 final String key = route == defaultRoute ? DEFAULT_KEY : route.getKey(); 083 appenders.put(key, new AppenderControl(appender, null, null)); 084 } else { 085 LOGGER.error("Appender " + route.getAppenderRef() + " cannot be located. Route ignored"); 086 } 087 } 088 } 089 super.start(); 090 } 091 092 @Override 093 public void stop() { 094 super.stop(); 095 final Map<String, Appender> map = config.getAppenders(); 096 for (final Map.Entry<String, AppenderControl> entry : appenders.entrySet()) { 097 final String name = entry.getValue().getAppender().getName(); 098 if (!map.containsKey(name)) { 099 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 }