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    /*
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     * org.apache.logging.log4j.core.LogEvent)
074     */
075    @Override
076    public void log(final Supplier<LoggerConfig> reconfigured, final LogEvent event) {
077        final LoggerConfig config = getActiveLoggerConfig(reconfigured);
078        try {
079            config.log(event);
080        } finally {
081            config.getReliabilityStrategy().afterLogEvent();
082        }
083    }
084
085    /*
086     * (non-Javadoc)
087     * 
088     * @see
089     * org.apache.logging.log4j.core.config.ReliabilityStrategy#beforeLogEvent(org.apache.logging.log4j.core.config.
090     * LoggerConfig, org.apache.logging.log4j.util.Supplier)
091     */
092    @Override
093    public LoggerConfig getActiveLoggerConfig(final Supplier<LoggerConfig> next) {
094        LoggerConfig result = this.loggerConfig;
095        if (!beforeLogEvent()) {
096            result = next.get();
097            return result.getReliabilityStrategy().getActiveLoggerConfig(next);
098        }
099        return result;
100    }
101
102    private boolean beforeLogEvent() {
103        return counter.incrementAndGet() > 0;
104    }
105
106    public void afterLogEvent() {
107        if (counter.decrementAndGet() == 0 && shutdown.get()) {
108            signalCompletionIfShutdown();
109        }
110    }
111
112    private void signalCompletionIfShutdown() {
113        final Lock lock = shutdownLock;
114        lock.lock();
115        try {
116            noLogEvents.signalAll();
117        } finally {
118            lock.unlock();
119        }
120    }
121
122    /*
123     * (non-Javadoc)
124     * 
125     * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#beforeStopAppenders()
126     */
127    @Override
128    public void beforeStopAppenders() {
129        waitForCompletion();
130    }
131
132    /**
133     * Waits for all log events to complete before returning.
134     */
135    private void waitForCompletion() {
136        shutdownLock.lock();
137        try {
138            if (shutdown.compareAndSet(false, true)) {
139                int retries = 0;
140                // repeat while counter is non-zero
141                while (!counter.compareAndSet(0, Integer.MIN_VALUE)) {
142
143                    // counter was non-zero
144                    if (counter.get() < 0) { // this should not happen
145                        return; // but if it does, we are already done
146                    }
147                    // counter greater than zero, wait for afterLogEvent to decrease count
148                    try {
149                        noLogEvents.await(retries + 1, TimeUnit.SECONDS);
150                    } catch (final InterruptedException ie) {
151                        if (++retries > MAX_RETRIES) {
152                            break;
153                        }
154                    }
155                }
156            }
157        } finally {
158            shutdownLock.unlock();
159        }
160    }
161
162    /*
163     * (non-Javadoc)
164     * 
165     * @see
166     * org.apache.logging.log4j.core.config.ReliabilityStrategy#beforeStopConfiguration(org.apache.logging.log4j.core
167     * .config.Configuration)
168     */
169    @Override
170    public void beforeStopConfiguration(Configuration configuration) {
171        // no action
172    }
173
174}