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      /*
51       * (non-Javadoc)
52       * 
53       * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#log(org.apache.logging.log4j.util.Supplier,
54       * java.lang.String, java.lang.String, org.apache.logging.log4j.Marker, org.apache.logging.log4j.Level,
55       * org.apache.logging.log4j.message.Message, java.lang.Throwable)
56       */
57      @Override
58      public void log(final Supplier<LoggerConfig> reconfigured, final String loggerName, final String fqcn,
59              final Marker marker, final Level level, final Message data, final Throwable t) {
60  
61          final LoggerConfig config = getActiveLoggerConfig(reconfigured);
62          try {
63              config.log(loggerName, fqcn, marker, level, data, t);
64          } finally {
65              config.getReliabilityStrategy().afterLogEvent();
66          }
67      }
68  
69      /*
70       * (non-Javadoc)
71       * 
72       * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#log(org.apache.logging.log4j.util.Supplier,
73       * org.apache.logging.log4j.core.LogEvent)
74       */
75      @Override
76      public void log(final Supplier<LoggerConfig> reconfigured, final LogEvent event) {
77          final LoggerConfig config = getActiveLoggerConfig(reconfigured);
78          try {
79              config.log(event);
80          } finally {
81              config.getReliabilityStrategy().afterLogEvent();
82          }
83      }
84  
85      /*
86       * (non-Javadoc)
87       * 
88       * @see
89       * org.apache.logging.log4j.core.config.ReliabilityStrategy#beforeLogEvent(org.apache.logging.log4j.core.config.
90       * LoggerConfig, org.apache.logging.log4j.util.Supplier)
91       */
92      @Override
93      public LoggerConfig getActiveLoggerConfig(final Supplier<LoggerConfig> next) {
94          LoggerConfig result = this.loggerConfig;
95          if (!beforeLogEvent()) {
96              result = next.get();
97              return result.getReliabilityStrategy().getActiveLoggerConfig(next);
98          }
99          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 }