From cb02c5f1a00b93d52afba0a12befb9f234202818 Mon Sep 17 00:00:00 2001 From: Ceki Gulcu Date: Tue, 3 Sep 2024 21:50:03 +0200 Subject: [PATCH] adding a simplified PropertyConfigurator Signed-off-by: Ceki Gulcu --- .../classic/joran/PropertyConfigurator.java | 173 ++++++++++++++++++ .../util/DefaultJoranConfigurator.java | 1 + .../joran/PropertyConfiguratorTest.java | 78 ++++++++ .../core/joran/GenericXMLConfigurator.java | 4 +- .../logback/core/spi/PropertyContainer.java | 6 +- .../core/joran/TrivialConfiguratorTest.java | 4 +- 6 files changed, 257 insertions(+), 9 deletions(-) create mode 100644 logback-classic/src/main/java/ch/qos/logback/classic/joran/PropertyConfigurator.java create mode 100644 logback-classic/src/test/java/ch/qos/logback/classic/joran/PropertyConfiguratorTest.java diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/PropertyConfigurator.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/PropertyConfigurator.java new file mode 100644 index 0000000000..c2db2f6aff --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/PropertyConfigurator.java @@ -0,0 +1,173 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2024, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.model.util.VariableSubstitutionsHelper; +import ch.qos.logback.core.spi.ContextAwareBase; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.*; + +import static ch.qos.logback.core.CoreConstants.DOT; + +public class PropertyConfigurator extends ContextAwareBase { + + static Comparator LENGTH_COMPARATOR = new Comparator() { + @Override + public int compare(String o1, String o2) { + int len1 = o1 == null ? 0 : o1.length(); + int len2 = o2 == null ? 0 : o2.length(); + // longer strings first + return len2 - len1; + } + }; + + static final String LOGBACK_PREFIX = "logback"; + static final String LOGBACK_ROOT_LOGGER_PREFIX = LOGBACK_PREFIX + DOT + "root"; + static final int LOGBACK_ROOT_LOGGER_PREFIX_LENGTH = LOGBACK_ROOT_LOGGER_PREFIX.length(); + + static final String LOGBACK_LOGGER_PREFIX = LOGBACK_PREFIX + DOT + "logger" + DOT; + static final int LOGBACK_LOGGER_PREFIX_LENGTH = LOGBACK_LOGGER_PREFIX.length(); + + VariableSubstitutionsHelper variableSubstitutionsHelper; + + LoggerContext getLoggerContext() { + return (LoggerContext) getContext(); + } + + @Override + public void setContext(Context context) { + super.setContext(context); + } + + void doConfigure(URL url) throws JoranException { + try { + URLConnection urlConnection = url.openConnection(); + // per http://jira.qos.ch/browse/LOGBACK-117 + // per http://jira.qos.ch/browse/LOGBACK-163 + urlConnection.setUseCaches(false); + InputStream in = urlConnection.getInputStream(); + doConfigure(in); + } catch (IOException ioe) { + String errMsg = "Could not open URL [" + url + "]."; + addError(errMsg, ioe); + throw new JoranException(errMsg, ioe); + } + } + + void doConfigure(String filename) throws JoranException { + try(FileInputStream fileInputStream = new FileInputStream(filename)) { + doConfigure(fileInputStream); + } catch (IOException e) { + throw new JoranException("Failed to load file "+filename, e); + } + } + + void doConfigure(InputStream inputStream) throws JoranException { + Properties props = new Properties(); + try { + props.load(inputStream); + } catch (IOException e) { + throw new JoranException("Failed to load from input stream", e); + } finally { + close(inputStream); + } + + doConfigure(props); + } + + private void close(InputStream inputStream) throws JoranException { + if(inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + throw new JoranException("failed to close stream", e); + } + } + } + + void doConfigure(Properties properties) { + Map variablesMap = extractVariablesMap(properties); + Map instructionMap = extractLogbackInstructionMap(properties); + + this.variableSubstitutionsHelper = new VariableSubstitutionsHelper(context, variablesMap); + configureLoggers(instructionMap); + configureRootLogger(instructionMap); + } + + void configureRootLogger(Map instructionMap) { + String val = subst(instructionMap.get(LOGBACK_ROOT_LOGGER_PREFIX)); + if (val != null) { + setLevel(org.slf4j.Logger.ROOT_LOGGER_NAME, val); + } + } + + void configureLoggers(Map instructionMap) { + + for (String key : instructionMap.keySet()) { + if (key.startsWith(LOGBACK_LOGGER_PREFIX)) { + String loggerName = key.substring(LOGBACK_LOGGER_PREFIX_LENGTH); + String value = subst(instructionMap.get(key)); + setLevel(loggerName, value); + } + } + } + + private void setLevel(String loggerName, String val) { + Logger logger = getLoggerContext().getLogger(loggerName); + Level level = Level.toLevel(val); + logger.setLevel(level); + } + + private Map extractVariablesMap(Properties properties) { + Map variablesMap = new HashMap<>(); + for (String key : properties.stringPropertyNames()) { + if (key != null && !key.startsWith(LOGBACK_PREFIX)) { + variablesMap.put(key, properties.getProperty(key)); + } + } + + return variablesMap; + } + + private Map extractLogbackInstructionMap(Properties properties) { + Map instructionMap = new TreeMap<>(LENGTH_COMPARATOR); + for (String key : properties.stringPropertyNames()) { + if (key != null && key.startsWith(LOGBACK_PREFIX)) { + instructionMap.put(key, properties.getProperty(key)); + } + } + return instructionMap; + } + + public String subst(String ref) { + + String substituted = variableSubstitutionsHelper.subst(ref); + if (ref != null && !ref.equals(substituted)) { + addInfo("value \"" + substituted + "\" substituted for \"" + ref + "\""); + } + return substituted; + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/DefaultJoranConfigurator.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/DefaultJoranConfigurator.java index 9a9db50a42..c6c4fbc8ee 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/util/DefaultJoranConfigurator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/DefaultJoranConfigurator.java @@ -98,6 +98,7 @@ private URL findConfigFileURLFromSystemProperties(ClassLoader classLoader, boole if (result != null) { return result; } + // if the above fails, try to find as a file File f = new File(logbackConfigFile); if (f.exists() && f.isFile()) { try { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/PropertyConfiguratorTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/PropertyConfiguratorTest.java new file mode 100644 index 0000000000..fa641d52a7 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/joran/PropertyConfiguratorTest.java @@ -0,0 +1,78 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2024, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PropertyConfiguratorTest { + + LoggerContext lc = new LoggerContext(); + Properties props = new Properties(); + PropertyConfigurator pc = new PropertyConfigurator(); + StatusPrinter2 statusPrinter2 = new StatusPrinter2(); + @BeforeEach + public void setup() throws Exception { + pc.setContext(lc); + } + + @Test + public void smoke() { + String TOTO_STR = "toto"; + props.setProperty(PropertyConfigurator.LOGBACK_ROOT_LOGGER_PREFIX, Level.INFO.levelStr); + props.setProperty(PropertyConfigurator.LOGBACK_LOGGER_PREFIX + TOTO_STR, Level.ERROR.levelStr); + pc.doConfigure(props); + + Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME); + Logger totoLogger = lc.getLogger(TOTO_STR); + + assertEquals(Level.INFO, rootLogger.getLevel()); + + assertEquals(Level.ERROR, totoLogger.getLevel()); + + } + + @Test + public void withVariables() { + String TOTO_STR = "toto"; + String ROOT_LEVEL_STR = "rootLevel"; + String TOTO_LEVEL_STR = "totoLevel"; + + props.setProperty(ROOT_LEVEL_STR, Level.INFO.levelStr); + System.setProperty("totoLevel", Level.ERROR.levelStr); + props.setProperty(PropertyConfigurator.LOGBACK_ROOT_LOGGER_PREFIX, asVar(ROOT_LEVEL_STR)); + props.setProperty(PropertyConfigurator.LOGBACK_LOGGER_PREFIX + TOTO_STR, asVar(TOTO_LEVEL_STR)); + pc.doConfigure(props); + + Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME); + Logger totoLogger = lc.getLogger(TOTO_STR); + statusPrinter2.print(lc); + assertEquals(Level.INFO, rootLogger.getLevel()); + assertEquals(Level.ERROR, totoLogger.getLevel()); + + } + + String asVar(String v) { + return "${"+v+"}"; + } +} \ No newline at end of file diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/GenericXMLConfigurator.java b/logback-core/src/main/java/ch/qos/logback/core/joran/GenericXMLConfigurator.java index 3680903a31..f2de1be1d0 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/GenericXMLConfigurator.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/GenericXMLConfigurator.java @@ -58,8 +58,8 @@ public final void doConfigure(URL url) throws JoranException { try { informContextOfURLUsedForConfiguration(getContext(), url); URLConnection urlConnection = url.openConnection(); - // per http://jira.qos.ch/browse/LBCORE-105 - // per http://jira.qos.ch/browse/LBCORE-127 + // per http://jira.qos.ch/browse/LOGBACK-117 LBCORE-105 + // per http://jira.qos.ch/browse/LOGBACK-163 LBCORE-127 urlConnection.setUseCaches(false); in = urlConnection.getInputStream(); diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/PropertyContainer.java b/logback-core/src/main/java/ch/qos/logback/core/spi/PropertyContainer.java index d9ae709ddb..474d0b3502 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/PropertyContainer.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/PropertyContainer.java @@ -29,10 +29,6 @@ default void addSubstitutionProperties(Properties props) { if (props == null) { return; } - for (Object keyObject : props.keySet()) { - String key = (String) keyObject; - String val = props.getProperty(key); - addSubstitutionProperty(key, val); - } + props.forEach((k, v) -> addSubstitutionProperty((String) k, (String) v)); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/TrivialConfiguratorTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/TrivialConfiguratorTest.java index 224714b984..1410501f0c 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/TrivialConfiguratorTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/TrivialConfiguratorTest.java @@ -104,7 +104,7 @@ public void illFormedXML() { } @Test - public void lbcore105() throws IOException, JoranException { + public void LOGBACK_117() throws IOException, JoranException { String jarEntry = "buzz.xml"; File jarFile = makeRandomJarFile(); fillInJarFile(jarFile, jarEntry); @@ -118,7 +118,7 @@ public void lbcore105() throws IOException, JoranException { } @Test - public void lbcore127() throws IOException, JoranException { + public void LOGBACK_163() throws IOException, JoranException { String jarEntry = "buzz.xml"; String jarEntry2 = "lightyear.xml";