001/* 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2026, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v2.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.classic.model.processor; 015 016import ch.qos.logback.classic.joran.ReconfigureOnChangeTask; 017import ch.qos.logback.classic.model.ConfigurationModel; 018import ch.qos.logback.core.Context; 019import ch.qos.logback.core.joran.spi.ConfigurationWatchList; 020import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; 021import ch.qos.logback.core.model.Model; 022import ch.qos.logback.core.model.processor.ModelHandlerBase; 023import ch.qos.logback.core.model.processor.ModelHandlerException; 024import ch.qos.logback.core.model.processor.ModelInterpretationContext; 025import ch.qos.logback.core.spi.ConfigurationEvent; 026import ch.qos.logback.core.util.Duration; 027import ch.qos.logback.core.util.OptionHelper; 028 029import java.util.concurrent.ScheduledExecutorService; 030import java.util.concurrent.ScheduledFuture; 031import java.util.concurrent.TimeUnit; 032 033/** 034 * This is a subclass of {@link ConfigurationModelHandler} offering configuration reloading support. 035 * 036 * <p>This class is also called by logback-tyler.</p> 037 * 038 */ 039public class ConfigurationModelHandlerFull extends ConfigurationModelHandler { 040 041 public static String FAILED_WATCH_PREDICATE_MESSAGE_1 = "Missing watchable .xml or .properties files."; 042 public static String FAILED_WATCH_PREDICATE_MESSAGE_2 = "Watching .xml files requires that the main configuration file is reachable as a URL"; 043 044 public ConfigurationModelHandlerFull(Context context) { 045 super(context); 046 } 047 048 static public ModelHandlerBase makeInstance2(Context context, ModelInterpretationContext mic) { 049 return new ConfigurationModelHandlerFull(context); 050 } 051 052 @Override 053 public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { 054 ConfigurationModel configurationModel = (ConfigurationModel) model; 055 056 // scanning is disabled 057 if (!(scanning == Boolean.TRUE)) { 058 return; 059 } 060 061 String scanPeriodStr = mic.subst(configurationModel.getScanPeriodStr()); 062 scheduleReconfigureOnChangeTask(scanPeriodStr); 063 064 ConfigurationWatchList cwl = ConfigurationWatchListUtil.getConfigurationWatchList(getContext()); 065 if (cwl != null) { 066 try { 067 addInfo("Main configuration file URL: " + cwl.getTopURL()); 068 addInfo("FileWatchList= {" + cwl.getFileWatchListAsStr() + "}"); 069 addInfo("URLWatchList= {" + cwl.getUrlWatchListAsStr() + "}"); 070 } catch (NoSuchMethodError e) { 071 addWarn("It looks like the version of logback-classic is more recent than"); 072 addWarn("the version of logback-core. Please align the two versions."); 073 } 074 } 075 } 076 077 078 /** 079 * This method is called from this class but also from logback-tyler. 080 * <p> 081 * This method assumes that the variables scanStr and scanPeriodStr have undergone variable substitution 082 * as applicable to their current environment 083 * 084 * @param scanPeriodStr 085 * @since 1.5.0 086 */ 087 public void detachedPostProcess(String scanStr, String scanPeriodStr) { 088 if (OptionHelper.isNullOrEmptyOrAllSpaces(scanStr) || "false".equalsIgnoreCase(scanStr)) { 089 return; 090 } 091 092 scheduleReconfigureOnChangeTask(scanPeriodStr); 093 } 094 095 private void scheduleReconfigureOnChangeTask(String scanPeriodStr) { 096 097 ScheduledExecutorService scheduledExecutorService = context.getScheduledExecutorService(); 098 boolean watchPredicateFulfilled = ConfigurationWatchListUtil.watchPredicateFulfilled(context); 099 if (!watchPredicateFulfilled) { 100 addWarn(FAILED_WATCH_PREDICATE_MESSAGE_1); 101 addWarn(FAILED_WATCH_PREDICATE_MESSAGE_2); 102 return; 103 } 104 ReconfigureOnChangeTask rocTask = new ReconfigureOnChangeTask(); 105 rocTask.setContext(context); 106 107 addInfo("Registering a new ReconfigureOnChangeTask " + rocTask); 108 109 context.fireConfigurationEvent(ConfigurationEvent.newConfigurationChangeDetectorRegisteredEvent(rocTask)); 110 111 Duration duration = getDurationOfScanPeriodAttribute(scanPeriodStr, SCAN_PERIOD_DEFAULT); 112 113 ConfigurationWatchList cwl = ConfigurationWatchListUtil.getConfigurationWatchList(context); 114 115 String fileWatchListAsStr = (cwl != null) ? cwl.getFileWatchListAsStr() : ""; 116 117 addInfo("Will scan for changes in [" + fileWatchListAsStr + "] "); 118 // Given that included files are encountered at a later phase, the complete list 119 // of files to scan can only be determined when the configuration is loaded in full. 120 // However, scan can be active if mainURL is set. Otherwise, when changes are 121 // detected the top level config file cannot be accessed. 122 addInfo("Setting ReconfigureOnChangeTask scanning period to " + duration); 123 124 ScheduledFuture<?> scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(rocTask, duration.getMilliseconds(), duration.getMilliseconds(), 125 TimeUnit.MILLISECONDS); 126 rocTask.setScheduledFuture(scheduledFuture); 127 context.addScheduledFuture(scheduledFuture); 128 129 } 130 131 private Duration getDurationOfScanPeriodAttribute(String scanPeriodAttrib, Duration defaultDuration) { 132 Duration duration = null; 133 134 if (!OptionHelper.isNullOrEmptyOrAllSpaces(scanPeriodAttrib)) { 135 try { 136 duration = Duration.valueOf(scanPeriodAttrib); 137 } catch (IllegalStateException | IllegalArgumentException e) { 138 addWarn("Failed to parse 'scanPeriod' attribute [" + scanPeriodAttrib + "]", e); 139 // default duration will be set below 140 } 141 } 142 143 if (duration == null) { 144 addInfo("No 'scanPeriod' specified. Defaulting to " + defaultDuration.toString()); 145 duration = defaultDuration; 146 } 147 return duration; 148 } 149}