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, LocationAwareReliabilityStrategy {
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       * java.lang.String, java.lang.String, java.lang.StackTraceElement, org.apache.logging.log4j.Marker,
74       * org.apache.logging.log4j.Level, org.apache.logging.log4j.message.Message, java.lang.Throwable)
75       */
76      @Override
77      public void log(final Supplier<LoggerConfig> reconfigured, final String loggerName, final String fqcn,
78          final StackTraceElement location, final Marker marker, final Level level, final Message data,
79          final Throwable t) {
80          final LoggerConfig config = getActiveLoggerConfig(reconfigured);
81          try {
82              config.log(loggerName, fqcn, location, marker, level, data, t);
83          } finally {
84              config.getReliabilityStrategy().afterLogEvent();
85          }
86      }
87  
88      /*
89       * (non-Javadoc)
90       *
91       * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#log(org.apache.logging.log4j.util.Supplier,
92       * org.apache.logging.log4j.core.LogEvent)
93       */
94      @Override
95      public void log(final Supplier<LoggerConfig> reconfigured, final LogEvent event) {
96          final LoggerConfig config = getActiveLoggerConfig(reconfigured);
97          try {
98              config.log(event);
99          } 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 }