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 return getFilter() != null && Filter.Result.DENY == getFilter().filter(event); 094 } 095 096 @PerformanceSensitive 097 private boolean isFilteredByLevel(final LogEvent event) { 098 return level != null && intLevel < event.getLevel().intLevel(); 099 } 100 101 @PerformanceSensitive 102 private boolean isRecursiveCall() { 103 if (recursive.get() != null) { 104 appenderErrorHandlerMessage("Recursive call to appender "); 105 return true; 106 } 107 return false; 108 } 109 110 private String appenderErrorHandlerMessage(final String prefix) { 111 final String result = createErrorMsg(prefix); 112 appender.getHandler().error(result); 113 return result; 114 } 115 116 private void callAppenderPreventRecursion(final LogEvent event) { 117 try { 118 recursive.set(this); 119 callAppender0(event); 120 } finally { 121 recursive.set(null); 122 } 123 } 124 125 private void callAppender0(final LogEvent event) { 126 ensureAppenderStarted(); 127 if (!isFilteredByAppender(event)) { 128 tryCallAppender(event); 129 } 130 } 131 132 private void ensureAppenderStarted() { 133 if (!appender.isStarted()) { 134 handleError("Attempted to append to non-started appender "); 135 } 136 } 137 138 private void handleError(final String prefix) { 139 final String msg = appenderErrorHandlerMessage(prefix); 140 if (!appender.ignoreExceptions()) { 141 throw new AppenderLoggingException(msg); 142 } 143 } 144 145 private String createErrorMsg(final String prefix) { 146 return prefix + appender.getName(); 147 } 148 149 private boolean isFilteredByAppender(final LogEvent event) { 150 return appender instanceof Filterable && ((Filterable) appender).isFiltered(event); 151 } 152 153 private void tryCallAppender(final LogEvent event) { 154 try { 155 appender.append(event); 156 } catch (final RuntimeException ex) { 157 handleAppenderError(ex); 158 } catch (final Exception ex) { 159 handleAppenderError(new AppenderLoggingException(ex)); 160 } 161 } 162 163 private void handleAppenderError(final RuntimeException ex) { 164 appender.getHandler().error(createErrorMsg("An exception occurred processing Appender "), ex); 165 if (!appender.ignoreExceptions()) { 166 throw ex; 167 } 168 } 169 170 // AppenderControl is a helper object whose purpose is to make it 171 // easier for LoggerConfig to manage and invoke Appenders. 172 // LoggerConfig manages Appenders by their name. To facilitate this, 173 // two AppenderControl objects are considered equal if and only 174 // if they have the same appender name. 175 @Override 176 public boolean equals(final Object obj) { 177 if (obj == this) { 178 return true; 179 } 180 if (!(obj instanceof AppenderControl)) { 181 return false; 182 } 183 final AppenderControl other = (AppenderControl) obj; 184 return Objects.equals(appenderName, other.appenderName); 185 } 186 187 @Override 188 public int hashCode() { 189 return appenderName.hashCode(); 190 } 191 192 @Override 193 public String toString() { 194 return super.toString() + "[appender=" + appender + ", appenderName=" + appenderName + ", level=" + level 195 + ", intLevel=" + intLevel + ", recursive=" + recursive + ", filter=" + getFilter() + "]"; 196 } 197}