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 */ 017 018package org.apache.logging.log4j.core.config; 019 020import java.util.Objects; 021import java.util.concurrent.TimeUnit; 022import java.util.concurrent.atomic.AtomicBoolean; 023import java.util.concurrent.atomic.AtomicInteger; 024import java.util.concurrent.locks.Condition; 025import java.util.concurrent.locks.Lock; 026import java.util.concurrent.locks.ReentrantLock; 027 028import org.apache.logging.log4j.Level; 029import org.apache.logging.log4j.Marker; 030import org.apache.logging.log4j.core.LogEvent; 031import org.apache.logging.log4j.message.Message; 032import org.apache.logging.log4j.util.Supplier; 033 034/** 035 * ReliabilityStrategy that counts the number of threads that have started to log an event but have not completed yet, 036 * and waits for these threads to finish before allowing the appenders to be stopped. 037 */ 038public class AwaitCompletionReliabilityStrategy implements ReliabilityStrategy, LocationAwareReliabilityStrategy { 039 private static final int MAX_RETRIES = 3; 040 private final AtomicInteger counter = new AtomicInteger(); 041 private final AtomicBoolean shutdown = new AtomicBoolean(false); 042 private final Lock shutdownLock = new ReentrantLock(); 043 private final Condition noLogEvents = shutdownLock.newCondition(); // should only be used when shutdown == true 044 private final LoggerConfig loggerConfig; 045 046 public AwaitCompletionReliabilityStrategy(final LoggerConfig loggerConfig) { 047 this.loggerConfig = Objects.requireNonNull(loggerConfig, "loggerConfig is null"); 048 } 049 050 /* 051 * (non-Javadoc) 052 * 053 * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#log(org.apache.logging.log4j.util.Supplier, 054 * java.lang.String, java.lang.String, org.apache.logging.log4j.Marker, org.apache.logging.log4j.Level, 055 * org.apache.logging.log4j.message.Message, java.lang.Throwable) 056 */ 057 @Override 058 public void log(final Supplier<LoggerConfig> reconfigured, final String loggerName, final String fqcn, 059 final Marker marker, final Level level, final Message data, final Throwable t) { 060 061 final LoggerConfig config = getActiveLoggerConfig(reconfigured); 062 try { 063 config.log(loggerName, fqcn, marker, level, data, t); 064 } finally { 065 config.getReliabilityStrategy().afterLogEvent(); 066 } 067 } 068 069 /* 070 * (non-Javadoc) 071 * 072 * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#log(org.apache.logging.log4j.util.Supplier, 073 * java.lang.String, java.lang.String, java.lang.StackTraceElement, org.apache.logging.log4j.Marker, 074 * org.apache.logging.log4j.Level, org.apache.logging.log4j.message.Message, java.lang.Throwable) 075 */ 076 @Override 077 public void log(final Supplier<LoggerConfig> reconfigured, final String loggerName, final String fqcn, 078 final StackTraceElement location, final Marker marker, final Level level, final Message data, 079 final Throwable t) { 080 final LoggerConfig config = getActiveLoggerConfig(reconfigured); 081 try { 082 config.log(loggerName, fqcn, location, marker, level, data, t); 083 } finally { 084 config.getReliabilityStrategy().afterLogEvent(); 085 } 086 } 087 088 /* 089 * (non-Javadoc) 090 * 091 * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#log(org.apache.logging.log4j.util.Supplier, 092 * org.apache.logging.log4j.core.LogEvent) 093 */ 094 @Override 095 public void log(final Supplier<LoggerConfig> reconfigured, final LogEvent event) { 096 final LoggerConfig config = getActiveLoggerConfig(reconfigured); 097 try { 098 config.log(event); 099 } finally { 100 config.getReliabilityStrategy().afterLogEvent(); 101 } 102 } 103 104 /* 105 * (non-Javadoc) 106 * 107 * @see 108 * org.apache.logging.log4j.core.config.ReliabilityStrategy#beforeLogEvent(org.apache.logging.log4j.core.config. 109 * LoggerConfig, org.apache.logging.log4j.util.Supplier) 110 */ 111 @Override 112 public LoggerConfig getActiveLoggerConfig(final Supplier<LoggerConfig> next) { 113 LoggerConfig result = this.loggerConfig; 114 if (!beforeLogEvent()) { 115 result = next.get(); 116 return result == this.loggerConfig ? result : result.getReliabilityStrategy().getActiveLoggerConfig(next); 117 } 118 return result; 119 } 120 121 private boolean beforeLogEvent() { 122 return counter.incrementAndGet() > 0; 123 } 124 125 @Override 126 public void afterLogEvent() { 127 if (counter.decrementAndGet() == 0 && shutdown.get()) { 128 signalCompletionIfShutdown(); 129 } 130 } 131 132 private void signalCompletionIfShutdown() { 133 final Lock lock = shutdownLock; 134 lock.lock(); 135 try { 136 noLogEvents.signalAll(); 137 } finally { 138 lock.unlock(); 139 } 140 } 141 142 /* 143 * (non-Javadoc) 144 * 145 * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#beforeStopAppenders() 146 */ 147 @Override 148 public void beforeStopAppenders() { 149 waitForCompletion(); 150 } 151 152 /** 153 * Waits for all log events to complete before returning. 154 */ 155 private void waitForCompletion() { 156 shutdownLock.lock(); 157 try { 158 if (shutdown.compareAndSet(false, true)) { 159 int retries = 0; 160 // repeat while counter is non-zero 161 while (!counter.compareAndSet(0, Integer.MIN_VALUE)) { 162 163 // counter was non-zero 164 if (counter.get() < 0) { // this should not happen 165 return; // but if it does, we are already done 166 } 167 // counter greater than zero, wait for afterLogEvent to decrease count 168 try { 169 noLogEvents.await(retries + 1, TimeUnit.SECONDS); 170 } catch (final InterruptedException ie) { 171 if (++retries > MAX_RETRIES) { 172 break; 173 } 174 } 175 } 176 } 177 } finally { 178 shutdownLock.unlock(); 179 } 180 } 181 182 /* 183 * (non-Javadoc) 184 * 185 * @see 186 * org.apache.logging.log4j.core.config.ReliabilityStrategy#beforeStopConfiguration(org.apache.logging.log4j.core 187 * .config.Configuration) 188 */ 189 @Override 190 public void beforeStopConfiguration(final Configuration configuration) { 191 // no action 192 } 193 194}