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 {
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    /* (non-Javadoc)
051     * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#log(org.apache.logging.log4j.util.Supplier, java.lang.String, java.lang.String, org.apache.logging.log4j.Marker, org.apache.logging.log4j.Level, org.apache.logging.log4j.message.Message, java.lang.Throwable)
052     */
053    @Override
054    public void log(final Supplier<LoggerConfig> reconfigured, final String loggerName, final String fqcn,
055            final Marker marker, final Level level, final Message data, final Throwable t) {
056
057        final LoggerConfig config = getActiveLoggerConfig(reconfigured);
058        try {
059            config.log(loggerName, fqcn, marker, level, data, t);
060        } finally {
061            config.getReliabilityStrategy().afterLogEvent();
062        }
063    }
064
065    /* (non-Javadoc)
066     * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#log(org.apache.logging.log4j.util.Supplier, org.apache.logging.log4j.core.LogEvent)
067     */
068    @Override
069    public void log(final Supplier<LoggerConfig> reconfigured, final LogEvent event) {
070        final LoggerConfig config = getActiveLoggerConfig(reconfigured);
071        try {
072            config.log(event);
073        } finally {
074            config.getReliabilityStrategy().afterLogEvent();
075        }
076    }
077
078    /* (non-Javadoc)
079     * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#beforeLogEvent(org.apache.logging.log4j.core.config.LoggerConfig, org.apache.logging.log4j.util.Supplier)
080     */
081    @Override
082    public LoggerConfig getActiveLoggerConfig(final Supplier<LoggerConfig> next) {
083        LoggerConfig result = this.loggerConfig;
084        if (!beforeLogEvent()) {
085            result = next.get();
086            return result.getReliabilityStrategy().getActiveLoggerConfig(next);
087        }
088        return result;
089    }
090
091    private boolean beforeLogEvent() {
092        return counter.incrementAndGet() > 0;
093    }
094
095    public void afterLogEvent() {
096        if (counter.decrementAndGet() == 0 && shutdown.get()) {
097            signalCompletionIfShutdown();
098        }
099    }
100
101    private void signalCompletionIfShutdown() {
102        final Lock lock = shutdownLock;
103        lock.lock();
104        try {
105            noLogEvents.signalAll();
106        } finally {
107            lock.unlock();
108        }
109    }
110
111    /* (non-Javadoc)
112     * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#beforeStopAppenders()
113     */
114    @Override
115    public void beforeStopAppenders() {
116        waitForCompletion();
117    }
118
119    /**
120     * Waits for all log events to complete before returning.
121     */
122    private void waitForCompletion() {
123        shutdownLock.lock();
124        try {
125            if (shutdown.compareAndSet(false, true)) {
126                int retries = 0;
127                // repeat while counter is non-zero
128                while (!counter.compareAndSet(0, Integer.MIN_VALUE)) {
129
130                    // counter was non-zero
131                    if (counter.get() < 0) { // this should not happen
132                        return; // but if it does, we are already done
133                    }
134                    // counter greater than zero, wait for afterLogEvent to decrease count
135                    try {
136                        noLogEvents.await(retries + 1, TimeUnit.SECONDS);
137                    } catch (final InterruptedException ie) {
138                        if (++retries > MAX_RETRIES) {
139                            break;
140                        }
141                    }
142                }
143            }
144        } finally {
145            shutdownLock.unlock();
146        }
147    }
148
149    /* (non-Javadoc)
150     * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#beforeStopConfiguration(org.apache.logging.log4j.core.config.Configuration)
151     */
152    @Override
153    public void beforeStopConfiguration(Configuration configuration) {
154        // no action
155    }
156
157}