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