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     @Override
107     public void afterLogEvent() {
108         if (counter.decrementAndGet() == 0 && shutdown.get()) {
109             signalCompletionIfShutdown();
110         }
111     }
112 
113     private void signalCompletionIfShutdown() {
114         final Lock lock = shutdownLock;
115         lock.lock();
116         try {
117             noLogEvents.signalAll();
118         } finally {
119             lock.unlock();
120         }
121     }
122 
123     /*
124      * (non-Javadoc)
125      * 
126      * @see org.apache.logging.log4j.core.config.ReliabilityStrategy#beforeStopAppenders()
127      */
128     @Override
129     public void beforeStopAppenders() {
130         waitForCompletion();
131     }
132 
133     /**
134      * Waits for all log events to complete before returning.
135      */
136     private void waitForCompletion() {
137         shutdownLock.lock();
138         try {
139             if (shutdown.compareAndSet(false, true)) {
140                 int retries = 0;
141                 // repeat while counter is non-zero
142                 while (!counter.compareAndSet(0, Integer.MIN_VALUE)) {
143 
144                     // counter was non-zero
145                     if (counter.get() < 0) { // this should not happen
146                         return; // but if it does, we are already done
147                     }
148                     // counter greater than zero, wait for afterLogEvent to decrease count
149                     try {
150                         noLogEvents.await(retries + 1, TimeUnit.SECONDS);
151                     } catch (final InterruptedException ie) {
152                         if (++retries > MAX_RETRIES) {
153                             break;
154                         }
155                     }
156                 }
157             }
158         } finally {
159             shutdownLock.unlock();
160         }
161     }
162 
163     /*
164      * (non-Javadoc)
165      * 
166      * @see
167      * org.apache.logging.log4j.core.config.ReliabilityStrategy#beforeStopConfiguration(org.apache.logging.log4j.core
168      * .config.Configuration)
169      */
170     @Override
171     public void beforeStopConfiguration(final Configuration configuration) {
172         // no action
173     }
174 
175 }