blob: 276d277adaeaec609dfcbfd34097f229994f22c3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.core.appender;
import org.apache.logging.log4j.LoggingException;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.AppenderControl;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttr;
import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* The FailoverAppender will capture exceptions in an Appender and then route the event
* to a different appender. Hopefully it is obvious that the Appenders must be configured
* to not suppress exceptions for the FailoverAppender to work.
*/
@Plugin(name = "Failover", type = "Core", elementType = "appender", printObject = true)
public final class FailoverAppender extends AppenderBase {
private final String primaryRef;
private final String[] failovers;
private final Configuration config;
private AppenderControl primary;
private List<AppenderControl> failoverAppenders = new ArrayList<AppenderControl>();
private FailoverAppender(String name, Filter filter, String primary, String[] failovers,
Configuration config, boolean handleExceptions) {
super(name, filter, null, handleExceptions);
this.primaryRef = primary;
this.failovers = failovers;
this.config = config;
}
@Override
public void start() {
Map<String, Appender> map = config.getAppenders();
int errors = 0;
if (map.containsKey(primaryRef)) {
primary = new AppenderControl(map.get(primaryRef));
} else {
LOGGER.error("Unable to locate primary Appender " + primaryRef);
++errors;
}
for (String name : failovers) {
if (map.containsKey(name)) {
failoverAppenders.add(new AppenderControl(map.get(name)));
} else {
LOGGER.error("Failover appender " + name + " is not configured");
}
}
if (failoverAppenders.size() == 0) {
LOGGER.error("No failover appenders are available");
++errors;
}
if (errors == 0) {
super.start();
}
}
/**
* Handle the Log event.
* @param event The LogEvent.
*/
public void append(LogEvent event) {
RuntimeException re = null;
if (!isStarted()) {
error("FailoverAppender " + getName() + " did not start successfully");
return;
}
try {
primary.callAppender(event);
} catch (Exception ex) {
re = new LoggingException(ex);
boolean written = false;
for (AppenderControl control : failoverAppenders) {
try {
control.callAppender(event);
written = true;
break;
} catch (Exception fex) {
// Try the next failover.
}
}
if (!written && !isExceptionSuppressed()) {
throw re;
}
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(getName());
sb.append(" primary=").append(primary).append(", failover={");
boolean first = true;
for (String str : failovers) {
if (!first) {
sb.append(", ");
}
sb.append(str);
first = false;
}
sb.append("}");
return sb.toString();
}
/**
* Create a Failover Appender.
* @param name The name of the Appender (required).
* @param primary The name of the primary Appender (required).
* @param failovers The name of one or more Appenders to fail over to (at least one is required).
* @param config The current Configuration (passed by the Configuration when the appender is created).
* @param filter A Filter (optional).
* @param suppress "true" if exceptions should be hidden from the application, "false" otherwise.
* The default is "true".
* @return The FailoverAppender that was created.
*/
@PluginFactory
public static FailoverAppender createAppender(@PluginAttr("name") String name,
@PluginAttr("primary") String primary,
@PluginElement("failovers") String[] failovers,
@PluginConfiguration Configuration config,
@PluginElement("filters") Filter filter,
@PluginAttr("suppressExceptions") String suppress) {
if (name == null) {
LOGGER.error("A name for the Appender must be specified");
return null;
}
if (primary == null) {
LOGGER.error("A primary Appender must be specified");
return null;
}
if (failovers == null || failovers.length == 0) {
LOGGER.error("At least one failover Appender must be specified");
return null;
}
boolean handleExceptions = suppress == null ? true : Boolean.valueOf(suppress);
return new FailoverAppender(name, filter, primary, failovers, config, handleExceptions);
}
}