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.config; 018 019import java.util.Objects; 020 021import org.apache.logging.log4j.Level; 022import org.apache.logging.log4j.core.Appender; 023import org.apache.logging.log4j.core.Filter; 024import org.apache.logging.log4j.core.LogEvent; 025import org.apache.logging.log4j.core.appender.AppenderLoggingException; 026import org.apache.logging.log4j.core.filter.AbstractFilterable; 027import org.apache.logging.log4j.core.filter.Filterable; 028import org.apache.logging.log4j.util.PerformanceSensitive; 029 030/** 031 * Wraps an {@link Appender} with details an appender implementation shouldn't need to know about. 032 */ 033public class AppenderControl extends AbstractFilterable { 034 035 /** 036 * The empty array. 037 */ 038 static final AppenderControl[] EMPTY_ARRAY = {}; 039 040 private final ThreadLocal<AppenderControl> recursive = new ThreadLocal<>(); 041 private final Appender appender; 042 private final Level level; 043 private final int intLevel; 044 private final String appenderName; 045 046 /** 047 * Constructor. 048 * 049 * @param appender The target Appender. 050 * @param level the Level to filter on. 051 * @param filter the Filter(s) to apply. 052 */ 053 public AppenderControl(final Appender appender, final Level level, final Filter filter) { 054 super(filter); 055 this.appender = appender; 056 this.appenderName = appender.getName(); 057 this.level = level; 058 this.intLevel = level == null ? Level.ALL.intLevel() : level.intLevel(); 059 start(); 060 } 061 062 /** 063 * Returns the name the appender had when this AppenderControl was constructed. 064 * 065 * @return the appender name 066 */ 067 public String getAppenderName() { 068 return appenderName; 069 } 070 071 /** 072 * Returns the Appender. 073 * 074 * @return the Appender. 075 */ 076 public Appender getAppender() { 077 return appender; 078 } 079 080 /** 081 * Call the appender. 082 * 083 * @param event The event to process. 084 */ 085 public void callAppender(final LogEvent event) { 086 if (shouldSkip(event)) { 087 return; 088 } 089 callAppenderPreventRecursion(event); 090 } 091 092 private boolean shouldSkip(final LogEvent event) { 093 return isFilteredByAppenderControl(event) || isFilteredByLevel(event) || isRecursiveCall(); 094 } 095 096 @PerformanceSensitive 097 private boolean isFilteredByAppenderControl(final LogEvent event) { 098 final Filter filter = getFilter(); 099 return filter != null && Filter.Result.DENY == filter.filter(event); 100 } 101 102 @PerformanceSensitive 103 private boolean isFilteredByLevel(final LogEvent event) { 104 return level != null && intLevel < event.getLevel().intLevel(); 105 } 106 107 @PerformanceSensitive 108 private boolean isRecursiveCall() { 109 if (recursive.get() != null) { 110 appenderErrorHandlerMessage("Recursive call to appender "); 111 return true; 112 } 113 return false; 114 } 115 116 private String appenderErrorHandlerMessage(final String prefix) { 117 final String result = createErrorMsg(prefix); 118 appender.getHandler().error(result); 119 return result; 120 } 121 122 private void callAppenderPreventRecursion(final LogEvent event) { 123 try { 124 recursive.set(this); 125 callAppender0(event); 126 } finally { 127 recursive.set(null); 128 } 129 } 130 131 private void callAppender0(final LogEvent event) { 132 ensureAppenderStarted(); 133 if (!isFilteredByAppender(event)) { 134 tryCallAppender(event); 135 } 136 } 137 138 private void ensureAppenderStarted() { 139 if (!appender.isStarted()) { 140 handleError("Attempted to append to non-started appender "); 141 } 142 } 143 144 private void handleError(final String prefix) { 145 final String msg = appenderErrorHandlerMessage(prefix); 146 if (!appender.ignoreExceptions()) { 147 throw new AppenderLoggingException(msg); 148 } 149 } 150 151 private String createErrorMsg(final String prefix) { 152 return prefix + appender.getName(); 153 } 154 155 private boolean isFilteredByAppender(final LogEvent event) { 156 return appender instanceof Filterable && ((Filterable) appender).isFiltered(event); 157 } 158 159 private void tryCallAppender(final LogEvent event) { 160 try { 161 appender.append(event); 162 } catch (final RuntimeException error) { 163 handleAppenderError(event, error); 164 } catch (final Throwable throwable) { 165 handleAppenderError(event, new AppenderLoggingException(throwable)); 166 } 167 } 168 169 private void handleAppenderError(final LogEvent event, final RuntimeException ex) { 170 appender.getHandler().error(createErrorMsg("An exception occurred processing Appender "), event, ex); 171 if (!appender.ignoreExceptions()) { 172 throw ex; 173 } 174 } 175 176 // AppenderControl is a helper object whose purpose is to make it 177 // easier for LoggerConfig to manage and invoke Appenders. 178 // LoggerConfig manages Appenders by their name. To facilitate this, 179 // two AppenderControl objects are considered equal if and only 180 // if they have the same appender name. 181 @Override 182 public boolean equals(final Object obj) { 183 if (obj == this) { 184 return true; 185 } 186 if (!(obj instanceof AppenderControl)) { 187 return false; 188 } 189 final AppenderControl other = (AppenderControl) obj; 190 return Objects.equals(appenderName, other.appenderName); 191 } 192 193 @Override 194 public int hashCode() { 195 return appenderName.hashCode(); 196 } 197 198 @Override 199 public String toString() { 200 return super.toString() + "[appender=" + appender + ", appenderName=" + appenderName + ", level=" + level 201 + ", intLevel=" + intLevel + ", recursive=" + recursive + ", filter=" + getFilter() + "]"; 202 } 203}