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