1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  package ch.qos.logback.classic.turbo;
15  
16  import static ch.qos.logback.core.CoreConstants.MILLIS_IN_ONE_SECOND;
17  
18  import java.io.File;
19  import java.net.URL;
20  import java.util.List;
21  
22  import org.slf4j.Marker;
23  
24  import ch.qos.logback.classic.Level;
25  import ch.qos.logback.classic.Logger;
26  import ch.qos.logback.classic.LoggerContext;
27  import ch.qos.logback.classic.joran.JoranConfigurator;
28  import ch.qos.logback.core.CoreConstants;
29  import ch.qos.logback.core.joran.spi.ConfigurationWatchList;
30  import ch.qos.logback.core.joran.spi.JoranException;
31  import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil;
32  import ch.qos.logback.core.model.Model;
33  import ch.qos.logback.core.model.ModelUtil;
34  import ch.qos.logback.core.spi.FilterReply;
35  import ch.qos.logback.core.status.StatusUtil;
36  
37  
38  
39  
40  
41  
42  
43  @Deprecated
44  public class ReconfigureOnChangeFilter extends TurboFilter {
45  
46      
47  
48  
49      
50      public final static long DEFAULT_REFRESH_PERIOD = 60 * MILLIS_IN_ONE_SECOND;
51  
52      long refreshPeriod = DEFAULT_REFRESH_PERIOD;
53      URL mainConfigurationURL;
54      protected volatile long nextCheck;
55  
56      ConfigurationWatchList configurationWatchList;
57  
58      @Override
59      public void start() {
60          configurationWatchList = ConfigurationWatchListUtil.getConfigurationWatchList(context);
61          if (configurationWatchList != null) {
62              mainConfigurationURL = configurationWatchList.getMainURL();
63              if (mainConfigurationURL == null) {
64                  addWarn("Due to missing top level configuration file, automatic reconfiguration is impossible.");
65                  return;
66              }
67              List<File> watchList = configurationWatchList.getCopyOfFileWatchList();
68              long inSeconds = refreshPeriod / 1000;
69              addInfo("Will scan for changes in [" + watchList + "] every " + inSeconds + " seconds. ");
70              synchronized (configurationWatchList) {
71                  updateNextCheck(System.currentTimeMillis());
72              }
73              super.start();
74          } else {
75              addWarn("Empty ConfigurationWatchList in context");
76          }
77      }
78  
79      @Override
80      public String toString() {
81          return "ReconfigureOnChangeFilter{" + "invocationCounter=" + invocationCounter + '}';
82      }
83  
84      
85      
86      
87      
88      
89      
90      private long invocationCounter = 0;
91  
92      private volatile long mask = 0xF;
93      private volatile long lastMaskCheck = System.currentTimeMillis();
94  
95      @Override
96      public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) {
97          if (!isStarted()) {
98              return FilterReply.NEUTRAL;
99          }
100 
101         
102         
103         
104         if (((invocationCounter++) & mask) != mask) {
105             return FilterReply.NEUTRAL;
106         }
107 
108         long now = System.currentTimeMillis();
109 
110         synchronized (configurationWatchList) {
111             updateMaskIfNecessary(now);
112             if (changeDetected(now)) {
113                 
114                 
115                 
116                 
117                 disableSubsequentReconfiguration();
118                 detachReconfigurationToNewThread();
119             }
120         }
121 
122         return FilterReply.NEUTRAL;
123     }
124 
125     
126     
127     
128     private static final int MAX_MASK = 0xFFFF;
129 
130     
131     
132     
133     private static final long MASK_INCREASE_THRESHOLD = 100;
134 
135     
136     
137     
138     private static final long MASK_DECREASE_THRESHOLD = MASK_INCREASE_THRESHOLD * 8;
139 
140     
141     
142     private void updateMaskIfNecessary(long now) {
143         final long timeElapsedSinceLastMaskUpdateCheck = now - lastMaskCheck;
144         lastMaskCheck = now;
145         if (timeElapsedSinceLastMaskUpdateCheck < MASK_INCREASE_THRESHOLD && (mask < MAX_MASK)) {
146             mask = (mask << 1) | 1;
147         } else if (timeElapsedSinceLastMaskUpdateCheck > MASK_DECREASE_THRESHOLD) {
148             mask = mask >>> 2;
149         }
150     }
151 
152     
153     
154     
155     void detachReconfigurationToNewThread() {
156         addInfo("Detected change in [" + configurationWatchList.getCopyOfFileWatchList() + "]");
157         context.getExecutorService().submit(new ReconfiguringThread());
158     }
159 
160     void updateNextCheck(long now) {
161         nextCheck = now + refreshPeriod;
162     }
163 
164     protected boolean changeDetected(long now) {
165         if (now >= nextCheck) {
166             updateNextCheck(now);
167             File file = configurationWatchList.changeDetected();
168             return file != null;
169         }
170         return false;
171     }
172 
173     void disableSubsequentReconfiguration() {
174         nextCheck = Long.MAX_VALUE;
175     }
176 
177     public long getRefreshPeriod() {
178         return refreshPeriod;
179     }
180 
181     public void setRefreshPeriod(long refreshPeriod) {
182         this.refreshPeriod = refreshPeriod;
183     }
184 
185     class ReconfiguringThread implements Runnable {
186         public void run() {
187             if (mainConfigurationURL == null) {
188                 addInfo("Due to missing top level configuration file, skipping reconfiguration");
189                 return;
190             }
191             LoggerContext lc = (LoggerContext) context;
192             addInfo(CoreConstants.RESET_MSG_PREFIX + "named [" + context.getName() + "]");
193             if (mainConfigurationURL.toString().endsWith("xml")) {
194                 performXMLConfiguration(lc);
195             } else if (mainConfigurationURL.toString().endsWith("groovy")) {
196                 addError("Groovy configuration disabled due to Java 9 compilation issues.");
197             }
198         }
199 
200         private void performXMLConfiguration(LoggerContext lc) {
201             JoranConfigurator jc = new JoranConfigurator();
202             jc.setContext(context);
203             StatusUtil statusUtil = new StatusUtil(context);
204             Model failSafeTop = jc.recallSafeConfiguration();
205             URL mainURL = ConfigurationWatchListUtil.getMainWatchURL(context);
206             lc.reset();
207             long threshold = System.currentTimeMillis();
208             try {
209                 jc.doConfigure(mainConfigurationURL);
210                 if (statusUtil.hasXMLParsingErrors(threshold)) {
211                     fallbackConfiguration(lc, failSafeTop, mainURL);
212                 }
213             } catch (JoranException e) {
214                 fallbackConfiguration(lc, failSafeTop, mainURL);
215             }
216         }
217 
218         private void fallbackConfiguration(LoggerContext lc, Model failSafeTop, URL mainURL) {
219             JoranConfigurator joranConfigurator = new JoranConfigurator();
220             joranConfigurator.setContext(context);
221             if (failSafeTop != null) {
222                 addWarn("Falling back to previously registered safe configuration.");
223                 try {
224                     lc.reset();
225                     JoranConfigurator.informContextOfURLUsedForConfiguration(context, mainURL);
226                     ModelUtil.resetForReuse(failSafeTop);
227                     joranConfigurator.processModel(failSafeTop);
228                     addInfo("Re-registering previous fallback configuration once more as a fallback configuration point");
229                     joranConfigurator.registerSafeConfiguration(failSafeTop);
230                 } catch (Exception e) {
231                     addError("Unexpected exception thrown by a configuration considered safe.", e);
232                 }
233             } else {
234                 addWarn("No previous configuration to fall back on.");
235             }
236         }
237     }
238 }