Skip to content

Commit

Permalink
Merge pull request #51 from avaje/feature/eventLog
Browse files Browse the repository at this point in the history
Add EventLog interface with preInitialisation() and postInitialisation() support
  • Loading branch information
rbygrave authored Feb 15, 2023
2 parents a282ff2 + e2875ff commit 0348e83
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 53 deletions.
4 changes: 0 additions & 4 deletions src/main/java/io/avaje/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import java.util.function.IntConsumer;
import java.util.function.LongConsumer;

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).
Expand All @@ -34,8 +32,6 @@
*/
public class Config {

static final System.Logger log = AppLog.getLogger("io.avaje.config");

private static final Configuration data = CoreConfiguration.initialise();

/**
Expand Down
32 changes: 21 additions & 11 deletions src/main/java/io/avaje/config/CoreConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
final class CoreConfiguration implements Configuration {

private final EventLog log;
private final ModifyAwareProperties properties;
private final Map<String, OnChangeListener> callbacks = new ConcurrentHashMap<>();
private final CoreListValue listValue;
Expand All @@ -26,11 +27,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);
Expand All @@ -41,19 +43,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() {
Expand Down Expand Up @@ -88,7 +97,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);
}
}

Expand Down Expand Up @@ -142,7 +151,7 @@ public Configuration forPath(String pathPrefix) {
newProps.put("", entry.getValue());
}
}
return new CoreConfiguration(newProps, dotPrefix);
return new CoreConfiguration(log, newProps, dotPrefix);
}

@Override
Expand Down Expand Up @@ -369,7 +378,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);
}
Expand Down Expand Up @@ -436,9 +444,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;
}

Expand All @@ -447,7 +457,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);
}
}
}
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/io/avaje/config/DefaultEventLog.java
Original file line number Diff line number Diff line change
@@ -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);
}

}
40 changes: 40 additions & 0 deletions src/main/java/io/avaje/config/EventLog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.avaje.config;

import java.lang.System.Logger.Level;

/**
* Configuration events are sent to this event log.
* <p>
* 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.
* <p>
* The message format is as per {@link java.text.MessageFormat#format(String, Object...)}.
*/
void log(Level level, String message, Object... args);

}
14 changes: 8 additions & 6 deletions src/main/java/io/avaje/config/FileWatch.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,22 @@

final class FileWatch {

private final EventLog log;
private final Configuration configuration;
private final YamlLoader yamlLoader;
private final List<Entry> files;
private final long delay;
private final long period;

FileWatch(Configuration configuration, List<File> loadedFiles, YamlLoader yamlLoader) {
FileWatch(CoreConfiguration configuration, List<File> 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);
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
}
}

Expand All @@ -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);
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/io/avaje/config/InitialLoadContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
final class InitialLoadContext {

private final EventLog log;
/**
* Map we are loading the properties into.
*/
Expand All @@ -30,7 +31,8 @@ final class InitialLoadContext {
private final List<File> loadedFiles = new ArrayList<>();
private final CoreExpressionEval exprEval;

InitialLoadContext() {
InitialLoadContext(EventLog log) {
this.log = log;
this.exprEval = new CoreExpressionEval(map);
}

Expand Down Expand Up @@ -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<String, String> entry : map.entrySet()) {
String key = entry.getKey();
Expand Down
14 changes: 8 additions & 6 deletions src/main/java/io/avaje/config/InitialLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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.");
}
}

Expand All @@ -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);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
requires transitive io.avaje.applog;
requires static org.yaml.snakeyaml;

uses io.avaje.config.EventLog;
uses io.avaje.config.ConfigurationSource;
}
12 changes: 6 additions & 6 deletions src/test/java/io/avaje/config/CoreConfigurationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ private Properties basicProperties() {
}

private CoreConfiguration createSample() {
return new CoreConfiguration(basicProperties());
return new CoreConfiguration(new DefaultEventLog(), basicProperties());
}

@Test
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();
Expand Down Expand Up @@ -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");
Expand All @@ -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( new CoreConfiguration(new DefaultEventLog(), new Properties()) , Collections.emptyList(), null));
data.loadIntoSystemProperties();
}

Expand Down Expand Up @@ -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");
Expand All @@ -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");
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/io/avaje/config/FileWatchTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<File> files() {
Expand Down
Loading

0 comments on commit 0348e83

Please sign in to comment.