View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  
18  package org.apache.logging.log4j.core.config;
19  
20  import java.util.Objects;
21  import java.util.concurrent.TimeUnit;
22  import java.util.concurrent.atomic.AtomicBoolean;
23  import java.util.concurrent.atomic.AtomicInteger;
24  import java.util.concurrent.locks.Condition;
25  import java.util.concurrent.locks.Lock;
26  import java.util.concurrent.locks.ReentrantLock;
27  
28  import org.apache.logging.log4j.Level;
29  import org.apache.logging.log4j.Marker;
30  import org.apache.logging.log4j.core.LogEvent;
31  import org.apache.logging.log4j.message.Message;
32  import org.apache.logging.log4j.util.Supplier;
33  
34  /**
35   * ReliabilityStrategy that counts the number of threads that have started to log an event but have not completed yet,
36   * and waits for these threads to finish before allowing the appenders to be stopped.
37   */
38  public class AwaitCompletionReliabilityStrategy implements ReliabilityStrategy {
39      private static final int MAX_RETRIES = 3;
40      private final AtomicInteger counter = new AtomicInteger();
41      private final AtomicBoolean shutdown = new AtomicBoolean(false);
42      private final Lock shutdownLock = new ReentrantLock();
43      private final Condition noLogEvents = shutdownLock.newCondition(); // should only be used when shutdown == true
44      private final LoggerConfig loggerConfig;
45  
46      public AwaitCompletionReliabilityStrategy(final LoggerConfig loggerConfig) {
47          this.loggerConfig = Objects.requireNonNull(loggerConfig, "loggerConfig is null");
48      }
49  
50      /* (non-Javadoc)
51       * @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)
52       */
53      @Override
54      public void log(final Supplier<LoggerConfig> reconfigured, final String loggerName, final String fqcn,
55              final Marker marker, final Level level, final Message data, final Throwable t) {
56  
57          final LoggerConfig config = getActiveLoggerConfig(reconfigured);
58          try {
59              config.log(loggerName, fqcn, marker, level, data, t);
60          } finally {
61              config.getReliabilityStrategy().afterLogEvent();
62          }
63      }
64  
65      /* (non-Javadoc)
66       * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#log(org.apache.logging.log4j.util.Supplier, org.apache.logging.log4j.core.LogEvent)
67       */
68      @Override
69      public void log(final Supplier<LoggerConfig> reconfigured, final LogEvent event) {
70          final LoggerConfig config = getActiveLoggerConfig(reconfigured);
71          try {
72              config.log(event);
73          } finally {
74              config.getReliabilityStrategy().afterLogEvent();
75          }
76      }
77  
78      /* (non-Javadoc)
79       * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#beforeLogEvent(org.apache.logging.log4j.core.config.LoggerConfig, org.apache.logging.log4j.util.Supplier)
80       */
81      @Override
82      public LoggerConfig getActiveLoggerConfig(final Supplier<LoggerConfig> next) {
83          LoggerConfig result = this.loggerConfig;
84          if (!beforeLogEvent()) {
85              result = next.get();
86              return result.getReliabilityStrategy().getActiveLoggerConfig(next);
87          }
88          return result;
89      }
90  
91      private boolean beforeLogEvent() {
92          return counter.incrementAndGet() > 0;
93      }
94  
95      public void afterLogEvent() {
96          if (counter.decrementAndGet() == 0 && shutdown.get()) {
97              signalCompletionIfShutdown();
98          }
99      }
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 }