blob: e6f1ce8e490492113ced7865d2453bcf87cb3895 [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.config;
import java.util.Objects;
import org.apache.logging.log4j.Level;
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.appender.AppenderLoggingException;
import org.apache.logging.log4j.core.filter.AbstractFilterable;
import org.apache.logging.log4j.core.filter.Filterable;
import org.apache.logging.log4j.util.PerformanceSensitive;
/**
* Wraps an {@link Appender} with details an appender implementation shouldn't need to know about.
*/
public class AppenderControl extends AbstractFilterable {
private final ThreadLocal<AppenderControl> recursive = new ThreadLocal<>();
private final Appender appender;
private final Level level;
private final int intLevel;
private final String appenderName;
/**
* Constructor.
*
* @param appender The target Appender.
* @param level the Level to filter on.
* @param filter the Filter(s) to apply.
*/
public AppenderControl(final Appender appender, final Level level, final Filter filter) {
super(filter, null);
this.appender = appender;
this.appenderName = appender.getName();
this.level = level;
this.intLevel = level == null ? Level.ALL.intLevel() : level.intLevel();
start();
}
/**
* Returns the name the appender had when this AppenderControl was constructed.
*
* @return the appender name
*/
public String getAppenderName() {
return appenderName;
}
/**
* Returns the Appender.
*
* @return the Appender.
*/
public Appender getAppender() {
return appender;
}
/**
* Call the appender.
*
* @param event The event to process.
*/
public void callAppender(final LogEvent event) {
if (shouldSkip(event)) {
return;
}
callAppenderPreventRecursion(event);
}
private boolean shouldSkip(final LogEvent event) {
return isFilteredByAppenderControl(event) || isFilteredByLevel(event) || isRecursiveCall();
}
@PerformanceSensitive
private boolean isFilteredByAppenderControl(final LogEvent event) {
final Filter filter = getFilter();
return filter != null && Filter.Result.DENY == filter.filter(event);
}
@PerformanceSensitive
private boolean isFilteredByLevel(final LogEvent event) {
return level != null && intLevel < event.getLevel().intLevel();
}
@PerformanceSensitive
private boolean isRecursiveCall() {
if (recursive.get() != null) {
appenderErrorHandlerMessage("Recursive call to appender ");
return true;
}
return false;
}
private String appenderErrorHandlerMessage(final String prefix) {
final String result = createErrorMsg(prefix);
appender.getHandler().error(result);
return result;
}
private void callAppenderPreventRecursion(final LogEvent event) {
try {
recursive.set(this);
callAppender0(event);
} finally {
recursive.set(null);
}
}
private void callAppender0(final LogEvent event) {
ensureAppenderStarted();
if (!isFilteredByAppender(event)) {
tryCallAppender(event);
}
}
private void ensureAppenderStarted() {
if (!appender.isStarted()) {
handleError("Attempted to append to non-started appender ");
}
}
private void handleError(final String prefix) {
final String msg = appenderErrorHandlerMessage(prefix);
if (!appender.ignoreExceptions()) {
throw new AppenderLoggingException(msg);
}
}
private String createErrorMsg(final String prefix) {
return prefix + appender.getName();
}
private boolean isFilteredByAppender(final LogEvent event) {
return appender instanceof Filterable && ((Filterable) appender).isFiltered(event);
}
private void tryCallAppender(final LogEvent event) {
try {
appender.append(event);
} catch (final RuntimeException error) {
handleAppenderError(event, error);
} catch (final Exception error) {
handleAppenderError(event, new AppenderLoggingException(error));
}
}
private void handleAppenderError(final LogEvent event, final RuntimeException ex) {
appender.getHandler().error(createErrorMsg("An exception occurred processing Appender "), event, ex);
if (!appender.ignoreExceptions()) {
throw ex;
}
}
// AppenderControl is a helper object whose purpose is to make it
// easier for LoggerConfig to manage and invoke Appenders.
// LoggerConfig manages Appenders by their name. To facilitate this,
// two AppenderControl objects are considered equal if and only
// if they have the same appender name.
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof AppenderControl)) {
return false;
}
final AppenderControl other = (AppenderControl) obj;
return Objects.equals(appenderName, other.appenderName);
}
@Override
public int hashCode() {
return appenderName.hashCode();
}
@Override
public String toString() {
return super.toString() + "[appender=" + appender + ", appenderName=" + appenderName + ", level=" + level
+ ", intLevel=" + intLevel + ", recursive=" + recursive + ", filter=" + getFilter() + "]";
}
}