diff --git a/src/main/java/io/avaje/config/Config.java b/src/main/java/io/avaje/config/Config.java index 6320095..9e31cfe 100644 --- a/src/main/java/io/avaje/config/Config.java +++ b/src/main/java/io/avaje/config/Config.java @@ -8,8 +8,6 @@ import java.util.Properties; import java.util.function.Consumer; -import io.avaje.applog.AppLog; - /** * Provides application Configuration based on loading properties and yaml files * as well as plugins that supply properties (like dynamic configuration loaded from a db). @@ -32,8 +30,6 @@ */ public class Config { - static final System.Logger log = AppLog.getLogger("io.avaje.config"); - private static final Configuration data = CoreConfiguration.initialise(); /** diff --git a/src/main/java/io/avaje/config/CoreConfiguration.java b/src/main/java/io/avaje/config/CoreConfiguration.java index 3d25f9d..4f6aeec 100644 --- a/src/main/java/io/avaje/config/CoreConfiguration.java +++ b/src/main/java/io/avaje/config/CoreConfiguration.java @@ -15,6 +15,7 @@ */ final class CoreConfiguration implements Configuration { + private final EventLog log; private final ModifyAwareProperties properties; private final Map callbacks = new ConcurrentHashMap<>(); private final CoreListValue listValue; @@ -24,11 +25,12 @@ final class CoreConfiguration implements Configuration { private Timer timer; private final String pathPrefix; - CoreConfiguration(Properties source) { - this(source, ""); + CoreConfiguration(EventLog log, Properties source) { + this(log, source, ""); } - CoreConfiguration(Properties source, String prefix) { + CoreConfiguration(EventLog log, Properties source, String prefix) { + this.log = log; this.properties = new ModifyAwareProperties(this, source); this.listValue = new CoreListValue(this); this.setValue = new CoreSetValue(this); @@ -39,19 +41,26 @@ final class CoreConfiguration implements Configuration { * Initialise the configuration which loads all the property sources. */ static Configuration initialise() { - final InitialLoader loader = new InitialLoader(); - CoreConfiguration configuration = new CoreConfiguration(loader.load()); + EventLog log = ServiceLoader.load(EventLog.class).findFirst().orElseGet(DefaultEventLog::new); + log.preInitialisation(); + final InitialLoader loader = new InitialLoader(log); + CoreConfiguration configuration = new CoreConfiguration(log, loader.load()); configuration.loadSources(); loader.initWatcher(configuration); configuration.initSystemProperties(); configuration.logMessage(loader); + log.postInitialisation(); return configuration; } + EventLog log() { + return log; + } + private void logMessage(InitialLoader loader) { String watchMsg = watcher == null ? "" : watcher.toString(); String intoMsg = loadedSystemProperties ? " into System properties" : ""; - Config.log.log(Level.INFO, "Loaded properties from {0}{1} {2}", loader.loadedFrom(), intoMsg, watchMsg); + log.log(Level.INFO, "Loaded properties from {0}{1} {2}", loader.loadedFrom(), intoMsg, watchMsg); } void initSystemProperties() { @@ -86,7 +95,7 @@ public void schedule(long delayMillis, long periodMillis, Runnable runnable) { if (timer == null) { timer = new Timer("ConfigTimer", true); } - timer.schedule(new Task(runnable), delayMillis, periodMillis); + timer.schedule(new Task(log, runnable), delayMillis, periodMillis); } } @@ -140,7 +149,7 @@ public Configuration forPath(String pathPrefix) { newProps.put("", entry.getValue()); } } - return new CoreConfiguration(newProps, dotPrefix); + return new CoreConfiguration(log, newProps, dotPrefix); } @Override @@ -367,7 +376,6 @@ void setProperty(String key, String newValue) { oldValue = properties.put(key, newValue); } if (!Objects.equals(newValue, oldValue)) { - Config.log.log(Level.TRACE, "setProperty key:{0} value:{1}", key, newValue); propertiesBoolCache.remove(key); config.fireOnChange(key, newValue); } @@ -434,9 +442,11 @@ Properties asProperties() { private static class Task extends TimerTask { + private final EventLog log; private final Runnable runnable; - private Task(Runnable runnable) { + private Task(EventLog log, Runnable runnable) { + this.log = log; this.runnable = runnable; } @@ -445,7 +455,7 @@ public void run() { try { runnable.run(); } catch (Exception e) { - Config.log.log(Level.ERROR, "Error executing timer task", e); + log.log(Level.ERROR, "Error executing timer task", e); } } } diff --git a/src/main/java/io/avaje/config/DefaultEventLog.java b/src/main/java/io/avaje/config/DefaultEventLog.java new file mode 100644 index 0000000..dc8fa19 --- /dev/null +++ b/src/main/java/io/avaje/config/DefaultEventLog.java @@ -0,0 +1,24 @@ +package io.avaje.config; + +import io.avaje.applog.AppLog; + +import java.lang.System.Logger.Level; + +/** + * Default implementation of EventLog just uses System.Logger. + */ +final class DefaultEventLog implements EventLog { + + private final System.Logger log = AppLog.getLogger("io.avaje.config"); + + @Override + public void log(Level level, String message, Throwable thrown) { + log.log(level, message, thrown); + } + + @Override + public void log(Level level, String message, Object... args) { + log.log(level, message, args); + } + +} diff --git a/src/main/java/io/avaje/config/EventLog.java b/src/main/java/io/avaje/config/EventLog.java new file mode 100644 index 0000000..5ddfcbb --- /dev/null +++ b/src/main/java/io/avaje/config/EventLog.java @@ -0,0 +1,40 @@ +package io.avaje.config; + +import java.lang.System.Logger.Level; + +/** + * Configuration events are sent to this event log. + *

+ * The EventLog implementation can be provided by ServiceLoader and then can + * control how the events are logged. For example, it might delay logging messages + * until logging implementation has finished configuration. + */ +public interface EventLog { + + /** + * Invoked when the configuration is being initialised. + */ + default void preInitialisation() { + // do nothing by default + } + + /** + * Invoked when the initialisation of configuration has been completed. + */ + default void postInitialisation() { + // do nothing by default + } + + /** + * Log an event with the given level, message, and thrown exception. + */ + void log(Level level, String message, Throwable thrown); + + /** + * Log an event with the given level, formatted message, and arguments. + *

+ * The message format is as per {@link java.text.MessageFormat#format(String, Object...)}. + */ + void log(Level level, String message, Object... args); + +} diff --git a/src/main/java/io/avaje/config/FileWatch.java b/src/main/java/io/avaje/config/FileWatch.java index caa9745..140f0e1 100644 --- a/src/main/java/io/avaje/config/FileWatch.java +++ b/src/main/java/io/avaje/config/FileWatch.java @@ -12,20 +12,22 @@ final class FileWatch { + private final EventLog log; private final Configuration configuration; private final YamlLoader yamlLoader; private final List files; private final long delay; private final long period; - FileWatch(Configuration configuration, List loadedFiles, YamlLoader yamlLoader) { + FileWatch(CoreConfiguration configuration, List loadedFiles, YamlLoader yamlLoader) { + this.log = configuration.log(); this.configuration = configuration; this.delay = configuration.getLong("config.watch.delay", 60); this.period = configuration.getInt("config.watch.period", 10); this.yamlLoader = yamlLoader; this.files = initFiles(loadedFiles); if (files.isEmpty()) { - Config.log.log(Level.ERROR, "No files to watch?"); + log.log(Level.ERROR, "No files to watch?"); } else { configuration.schedule(delay * 1000, period * 1000, this::check); } @@ -56,7 +58,7 @@ boolean changed() { void check() { for (Entry file : files) { if (file.reload()) { - Config.log.log(Level.DEBUG, "reloading configuration from {0}", file); + log.log(Level.DEBUG, "reloading configuration from {0}", file); if (file.isYaml()) { reloadYaml(file); } else { @@ -72,7 +74,7 @@ private void reloadProps(Entry file) { properties.load(is); put(properties); } catch (Exception e) { - Config.log.log(Level.ERROR, "Unexpected error reloading config file " + file, e); + log.log(Level.ERROR, "Unexpected error reloading config file " + file, e); } } @@ -87,12 +89,12 @@ private void put(Properties properties) { private void reloadYaml(Entry file) { if (yamlLoader == null) { - Config.log.log(Level.ERROR, "Unexpected - no yamlLoader to reload config file " + file); + log.log(Level.ERROR, "Unexpected - no yamlLoader to reload config file " + file); } else { try (InputStream is = file.inputStream()) { yamlLoader.load(is).forEach(configuration::setProperty); } catch (Exception e) { - Config.log.log(Level.ERROR, "Unexpected error reloading config file " + file, e); + log.log(Level.ERROR, "Unexpected error reloading config file " + file, e); } } } diff --git a/src/main/java/io/avaje/config/InitialLoadContext.java b/src/main/java/io/avaje/config/InitialLoadContext.java index 7e84795..30205d1 100644 --- a/src/main/java/io/avaje/config/InitialLoadContext.java +++ b/src/main/java/io/avaje/config/InitialLoadContext.java @@ -18,6 +18,7 @@ */ final class InitialLoadContext { + private final EventLog log; /** * Map we are loading the properties into. */ @@ -30,7 +31,8 @@ final class InitialLoadContext { private final List loadedFiles = new ArrayList<>(); private final CoreExpressionEval exprEval; - InitialLoadContext() { + InitialLoadContext(EventLog log) { + this.log = log; this.exprEval = new CoreExpressionEval(map); } @@ -124,7 +126,7 @@ void put(String key, String val) { * Evaluate all the expressions and return as a Properties object. */ Properties evalAll() { - Config.log.log(Level.TRACE, "load from {0}", loadedResources); + log.log(Level.TRACE, "load from {0}", loadedResources); Properties properties = new Properties(); for (Map.Entry entry : map.entrySet()) { String key = entry.getKey(); diff --git a/src/main/java/io/avaje/config/InitialLoader.java b/src/main/java/io/avaje/config/InitialLoader.java index bad5ec0..0fe522a 100644 --- a/src/main/java/io/avaje/config/InitialLoader.java +++ b/src/main/java/io/avaje/config/InitialLoader.java @@ -33,11 +33,13 @@ enum Source { FILE } - private final InitialLoadContext loadContext = new InitialLoadContext(); - + private final EventLog log; + private final InitialLoadContext loadContext; private YamlLoader yamlLoader; - InitialLoader() { + InitialLoader(EventLog log) { + this.log = log; + this.loadContext = new InitialLoadContext(log); initYamlLoader(); } @@ -184,7 +186,7 @@ private boolean loadTest() { loadYaml("application-test.yaml", RESOURCE); loadYaml("application-test.yml", RESOURCE); if (loadProperties("test-ebean.properties", RESOURCE)) { - Config.log.log(Level.WARNING, "Loading properties from test-ebean.properties is deprecated. Please migrate to application-test.yaml or application-test.properties instead."); + log.log(Level.WARNING, "Loading properties from test-ebean.properties is deprecated. Please migrate to application-test.yaml or application-test.properties instead."); } return loadContext.size() > before; } @@ -221,7 +223,7 @@ private void loadMain(Source source) { loadYaml("application.yml", source); loadProperties("application.properties", source); if (loadProperties("ebean.properties", source)) { - Config.log.log(Level.WARNING, "Loading properties from ebean.properties is deprecated. Please migrate to use application.yaml or application.properties instead."); + log.log(Level.WARNING, "Loading properties from ebean.properties is deprecated. Please migrate to use application.yaml or application.properties instead."); } } @@ -231,7 +233,7 @@ private void loadViaSystemProperty() { fileName = System.getProperty("props.file"); if (fileName != null) { if (!loadWithExtensionCheck(fileName)) { - Config.log.log(Level.WARNING, "Unable to find file {0} to load properties", fileName); + log.log(Level.WARNING, "Unable to find file {0} to load properties", fileName); } } } diff --git a/src/test/java/io/avaje/config/CoreConfigurationTest.java b/src/test/java/io/avaje/config/CoreConfigurationTest.java index f9542bb..4c73382 100644 --- a/src/test/java/io/avaje/config/CoreConfigurationTest.java +++ b/src/test/java/io/avaje/config/CoreConfigurationTest.java @@ -32,7 +32,7 @@ private Properties basicProperties() { } private CoreConfiguration createSample() { - return new CoreConfiguration(basicProperties()); + return new CoreConfiguration(new DefaultEventLog(), basicProperties()); } @Test @@ -40,7 +40,7 @@ void asProperties() { final Properties properties = basicProperties(); System.setProperty("SetViaSystemProperty", "FooBar"); - final CoreConfiguration configuration = new CoreConfiguration(properties); + final CoreConfiguration configuration = new CoreConfiguration(new DefaultEventLog(), properties); configuration.initSystemProperties(); final Properties loaded = configuration.asProperties(); @@ -85,7 +85,7 @@ void forPath_nested() { properties.setProperty("oneNot", "fried"); properties.setProperty("modify", "me"); - CoreConfiguration base = new CoreConfiguration(properties); + CoreConfiguration base = new CoreConfiguration(new DefaultEventLog(), properties); assertThat(base.size()).isEqualTo(9); Configuration one = base.forPath("one"); @@ -106,7 +106,7 @@ void forPath_nested() { @Test void test_toString() { assertThat(data.toString()).isNotEmpty(); - data.setWatcher(new FileWatch( Mockito.mock(Configuration.class), Collections.emptyList(), null)); + data.setWatcher(new FileWatch( Mockito.mock(CoreConfiguration.class), Collections.emptyList(), null)); data.loadIntoSystemProperties(); } @@ -328,7 +328,7 @@ void evalProperties() { final Properties properties = basicProperties(); properties.setProperty("someA", "before-${foo.bar}-after"); - final CoreConfiguration config = new CoreConfiguration(new Properties()); + final CoreConfiguration config = new CoreConfiguration(new DefaultEventLog(), new Properties()); final Properties copy = config.eval(properties); assertThat(copy.getProperty("someA")).isEqualTo("before-42-after"); @@ -342,7 +342,7 @@ void evalModify() { String beforeYeahNahValue = properties.getProperty("yeahNah"); - final CoreConfiguration config = new CoreConfiguration(new Properties()); + final CoreConfiguration config = new CoreConfiguration(new DefaultEventLog(), new Properties()); config.evalModify(properties); String someAValue = properties.getProperty("someA"); diff --git a/src/test/java/io/avaje/config/FileWatchTest.java b/src/test/java/io/avaje/config/FileWatchTest.java index ba8c226..40f12be 100644 --- a/src/test/java/io/avaje/config/FileWatchTest.java +++ b/src/test/java/io/avaje/config/FileWatchTest.java @@ -161,7 +161,7 @@ private CoreConfiguration newConfig() { final Properties properties = new Properties(); properties.setProperty("config.watch.delay", "1"); properties.setProperty("config.watch.period", "1"); - return new CoreConfiguration(properties); + return new CoreConfiguration(new DefaultEventLog(), properties); } private List files() { diff --git a/src/test/java/io/avaje/config/InitialLoaderTest.java b/src/test/java/io/avaje/config/InitialLoaderTest.java index c388917..a2cf138 100644 --- a/src/test/java/io/avaje/config/InitialLoaderTest.java +++ b/src/test/java/io/avaje/config/InitialLoaderTest.java @@ -15,7 +15,7 @@ void load() { String userName = System.getProperty("user.name"); String userHome = System.getProperty("user.home"); - InitialLoader loader = new InitialLoader(); + InitialLoader loader = new InitialLoader(new DefaultEventLog()); loader.loadProperties("test-properties/application.properties", RESOURCE); loader.loadYaml("test-properties/application.yaml", RESOURCE); @@ -37,7 +37,7 @@ void load() { @Test void loadWithExtensionCheck() { - InitialLoader loader = new InitialLoader(); + InitialLoader loader = new InitialLoader(new DefaultEventLog()); loader.loadWithExtensionCheck("test-dummy.properties"); loader.loadWithExtensionCheck("test-dummy.yml"); loader.loadWithExtensionCheck("test-dummy2.yaml"); @@ -50,7 +50,7 @@ void loadWithExtensionCheck() { @Test void loadYaml() { - InitialLoader loader = new InitialLoader(); + InitialLoader loader = new InitialLoader(new DefaultEventLog()); loader.loadYaml("test-properties/foo.yml", RESOURCE); Properties properties = loader.eval(); @@ -62,7 +62,7 @@ void loadProperties() { System.setProperty("eureka.instance.hostname", "host1"); System.setProperty("server.port", "9876"); - InitialLoader loader = new InitialLoader(); + InitialLoader loader = new InitialLoader(new DefaultEventLog()); loader.loadProperties("test-properties/one.properties", RESOURCE); Properties properties = loader.eval(); @@ -76,7 +76,7 @@ void loadProperties() { @Test void splitPaths() { - InitialLoader loader = new InitialLoader(); + InitialLoader loader = new InitialLoader(new DefaultEventLog()); assertThat(loader.splitPaths("one two three")).contains("one", "two", "three"); assertThat(loader.splitPaths("one,two,three")).contains("one", "two", "three"); assertThat(loader.splitPaths("one;two;three")).contains("one", "two", "three"); @@ -85,7 +85,7 @@ void splitPaths() { @Test void loadViaCommandLine_whenNotValid() { - InitialLoader loader = new InitialLoader(); + InitialLoader loader = new InitialLoader(new DefaultEventLog()); loader.loadViaCommandLine(new String[]{"-p", "8765"}); assertEquals(0, loader.size()); loader.loadViaCommandLine(new String[]{"-port", "8765"}); @@ -101,7 +101,7 @@ void loadViaCommandLine_whenNotValid() { @Test void loadViaCommandLine_localFile() { - InitialLoader loader = new InitialLoader(); + InitialLoader loader = new InitialLoader(new DefaultEventLog()); loader.loadViaCommandLine(new String[]{"-p", "test-dummy2.yaml"}); assertEquals(1, loader.size()); } @@ -110,13 +110,13 @@ void loadViaCommandLine_localFile() { void load_withSuppressTestResource() { //application-test.yaml is loaded when suppressTestResource is not set to true System.setProperty("suppressTestResource", ""); - InitialLoader loader = new InitialLoader(); + InitialLoader loader = new InitialLoader(new DefaultEventLog()); Properties properties = loader.load(); assertThat(properties.getProperty("myapp.activateFoo")).isEqualTo("true"); //application-test.yaml is not loaded when suppressTestResource is set to true System.setProperty("suppressTestResource", "true"); - InitialLoader loaderWithSuppressTestResource = new InitialLoader(); + InitialLoader loaderWithSuppressTestResource = new InitialLoader(new DefaultEventLog()); Properties propertiesWithoutTestResource = loaderWithSuppressTestResource.load(); assertThat(propertiesWithoutTestResource.getProperty("myapp.activateFoo")).isNull(); }