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