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; 018 019 import org.apache.logging.log4j.LoggingException; 020 import org.apache.logging.log4j.core.Appender; 021 import org.apache.logging.log4j.core.Filter; 022 import org.apache.logging.log4j.core.LogEvent; 023 import org.apache.logging.log4j.core.config.AppenderControl; 024 import org.apache.logging.log4j.core.config.Configuration; 025 import org.apache.logging.log4j.core.config.plugins.Plugin; 026 import org.apache.logging.log4j.core.config.plugins.PluginAttr; 027 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 028 import org.apache.logging.log4j.core.config.plugins.PluginElement; 029 import org.apache.logging.log4j.core.config.plugins.PluginFactory; 030 031 import java.util.ArrayList; 032 import java.util.List; 033 import java.util.Map; 034 035 /** 036 * The FailoverAppender will capture exceptions in an Appender and then route the event 037 * to a different appender. Hopefully it is obvious that the Appenders must be configured 038 * to not suppress exceptions for the FailoverAppender to work. 039 */ 040 @Plugin(name = "Failover", type = "Core", elementType = "appender", printObject = true) 041 public final class FailoverAppender extends AbstractAppender { 042 043 private final String primaryRef; 044 045 private final String[] failovers; 046 047 private final Configuration config; 048 049 private AppenderControl primary; 050 051 private final List<AppenderControl> failoverAppenders = new ArrayList<AppenderControl>(); 052 053 private FailoverAppender(String name, Filter filter, String primary, String[] failovers, 054 Configuration config, boolean handleExceptions) { 055 super(name, filter, null, handleExceptions); 056 this.primaryRef = primary; 057 this.failovers = failovers; 058 this.config = config; 059 } 060 061 062 @Override 063 public void start() { 064 Map<String, Appender> map = config.getAppenders(); 065 int errors = 0; 066 if (map.containsKey(primaryRef)) { 067 primary = new AppenderControl(map.get(primaryRef), null, null); 068 } else { 069 LOGGER.error("Unable to locate primary Appender " + primaryRef); 070 ++errors; 071 } 072 for (String name : failovers) { 073 if (map.containsKey(name)) { 074 failoverAppenders.add(new AppenderControl(map.get(name), null, null)); 075 } else { 076 LOGGER.error("Failover appender " + name + " is not configured"); 077 } 078 } 079 if (failoverAppenders.size() == 0) { 080 LOGGER.error("No failover appenders are available"); 081 ++errors; 082 } 083 if (errors == 0) { 084 super.start(); 085 } 086 } 087 088 /** 089 * Handle the Log event. 090 * @param event The LogEvent. 091 */ 092 public void append(LogEvent event) { 093 RuntimeException re = null; 094 if (!isStarted()) { 095 error("FailoverAppender " + getName() + " did not start successfully"); 096 return; 097 } 098 try { 099 primary.callAppender(event); 100 } catch (Exception ex) { 101 re = new LoggingException(ex); 102 boolean written = false; 103 for (AppenderControl control : failoverAppenders) { 104 try { 105 control.callAppender(event); 106 written = true; 107 break; 108 } catch (Exception fex) { 109 continue; 110 } 111 } 112 if (!written && !isExceptionSuppressed()) { 113 throw re; 114 } 115 } 116 } 117 118 @Override 119 public String toString() { 120 StringBuilder sb = new StringBuilder(getName()); 121 sb.append(" primary=").append(primary).append(", failover={"); 122 boolean first = true; 123 for (String str : failovers) { 124 if (!first) { 125 sb.append(", "); 126 } 127 sb.append(str); 128 first = false; 129 } 130 sb.append("}"); 131 return sb.toString(); 132 } 133 134 /** 135 * Create a Failover Appender. 136 * @param name The name of the Appender (required). 137 * @param primary The name of the primary Appender (required). 138 * @param failovers The name of one or more Appenders to fail over to (at least one is required). 139 * @param config The current Configuration (passed by the Configuration when the appender is created). 140 * @param filter A Filter (optional). 141 * @param suppress "true" if exceptions should be hidden from the application, "false" otherwise. 142 * The default is "true". 143 * @return The FailoverAppender that was created. 144 */ 145 @PluginFactory 146 public static FailoverAppender createAppender(@PluginAttr("name") String name, 147 @PluginAttr("primary") String primary, 148 @PluginElement("failovers") String[] failovers, 149 @PluginConfiguration Configuration config, 150 @PluginElement("filters") Filter filter, 151 @PluginAttr("suppressExceptions") String suppress) { 152 if (name == null) { 153 LOGGER.error("A name for the Appender must be specified"); 154 return null; 155 } 156 if (primary == null) { 157 LOGGER.error("A primary Appender must be specified"); 158 return null; 159 } 160 if (failovers == null || failovers.length == 0) { 161 LOGGER.error("At least one failover Appender must be specified"); 162 return null; 163 } 164 165 boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress); 166 167 return new FailoverAppender(name, filter, primary, failovers, config, handleExceptions); 168 } 169 }