diff --git a/gradle.properties b/gradle.properties index 8e1e8a4c0fe..d205b0a73f6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -43,7 +43,7 @@ developers=Graeme Rocher kapt.use.worker.api=true # Dependency Versions -micronautMavenPluginVersion=3.5.2 +micronautMavenPluginVersion=3.5.3 chromedriverVersion=79.0.3945.36 geckodriverVersion=0.26.0 webdriverBinariesVersion=1.4 @@ -52,7 +52,7 @@ kotlin.stdlib.default.dependency=false # For the docs graalVersion=22.0.0.2 -micronautSecurityVersion=3.9.3 +micronautSecurityVersion=3.9.4 org.gradle.caching=true org.gradle.parallel=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e241f149059..ae873c82885 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -73,7 +73,7 @@ managed-micronaut-cache = "3.5.0" managed-micronaut-cassandra = "5.1.1" managed-micronaut-coherence = "3.7.2" managed-micronaut-crac = "1.2.0" -managed-micronaut-data = "3.9.6" +managed-micronaut-data = "3.9.7" managed-micronaut-discovery = "3.3.0" managed-micronaut-elasticsearch = "4.3.0" managed-micronaut-email = "1.5.0" @@ -100,7 +100,7 @@ managed-micronaut-neo4j = "5.2.0" managed-micronaut-nats = "3.1.0" managed-micronaut-netflix = "2.1.0" managed-micronaut-object-storage = "1.1.0" -managed-micronaut-openapi = "4.8.5" +managed-micronaut-openapi = "4.8.6" managed-micronaut-oraclecloud = "2.3.4" managed-micronaut-picocli = "4.3.0" managed-micronaut-problem = "2.6.0" @@ -112,7 +112,7 @@ managed-micronaut-rss = "3.2.0" managed-micronaut-rxjava1 = "1.0.0" managed-micronaut-rxjava2 = "1.3.0" managed-micronaut-rxjava3 = "2.4.0" -managed-micronaut-security = "3.9.3" +managed-micronaut-security = "3.9.4" managed-micronaut-serialization = "1.5.2" managed-micronaut-servlet = "3.3.5" managed-micronaut-spring = "4.5.1" @@ -122,11 +122,11 @@ managed-micronaut-test-resources = "1.2.3" managed-micronaut-toml = "1.1.3" managed-micronaut-tracing = "4.5.0" managed-micronaut-tracing-legacy = "3.2.7" -managed-micronaut-views = "3.8.1" +managed-micronaut-views = "3.8.2" managed-micronaut-xml = "3.2.0" managed-neo4j = "3.5.35" managed-neo4j-java-driver = "4.4.9" -managed-netty = "4.1.87.Final" +managed-netty = "4.1.90.Final" managed-reactive-pg-client = "0.11.4" managed-reactive-streams = "1.0.4" # This should be kept aligned with https://github.com/micronaut-projects/micronaut-reactor/blob/master/gradle.properties from the BOM diff --git a/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/binding/HttpResponseSpec.groovy b/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/binding/HttpResponseSpec.groovy index df3fe92a1cc..02005fb800c 100644 --- a/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/binding/HttpResponseSpec.groovy +++ b/http-server-netty/src/test/groovy/io/micronaut/http/server/netty/binding/HttpResponseSpec.groovy @@ -153,11 +153,10 @@ class HttpResponseSpec extends AbstractMicronautSpec { HttpHeaders headers = response.headers then: // The content length header was replaced, not appended - !headers.names().contains("content-type") - !headers.names().contains("Content-Length") - headers.contains("content-length") response.header("Content-Type") == "text/plain" response.header("Content-Length") == "3" + response.header("content-type") == "text/plain" + response.header("content-length") == "3" } void "test server header"() { diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/staticresources/StaticResourceTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/staticresources/StaticResourceTest.java new file mode 100644 index 00000000000..853fb27ead9 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/staticresources/StaticResourceTest.java @@ -0,0 +1,52 @@ +/* + * Copyright 2017-2023 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.micronaut.http.server.tck.tests.staticresources; + +import io.micronaut.core.util.CollectionUtils; +import io.micronaut.http.HttpHeaders; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.MediaType; +import io.micronaut.http.server.tck.AssertionUtils; +import io.micronaut.http.uri.UriBuilder; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.Collections; + +import static io.micronaut.http.server.tck.TestScenario.asserts; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" +}) +public class StaticResourceTest { + public static final String SPEC_NAME = "StaticResourceTest"; + + @Test + public void staticResource() throws IOException { + asserts(SPEC_NAME, + CollectionUtils.mapOf( + "micronaut.router.static-resources.assets.mapping", "/assets/**", + "micronaut.router.static-resources.assets.paths", "classpath:assets"), + HttpRequest.GET(UriBuilder.of("/assets").path("hello.txt").build()).accept(MediaType.TEXT_PLAIN), + (server, request) -> AssertionUtils.assertDoesNotThrow(server, request, + HttpStatus.OK, + "Hello World", + Collections.singletonMap(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN))); + } +} diff --git a/http-server-tck/src/main/resources/assets/hello.txt b/http-server-tck/src/main/resources/assets/hello.txt new file mode 100644 index 00000000000..557db03de99 --- /dev/null +++ b/http-server-tck/src/main/resources/assets/hello.txt @@ -0,0 +1 @@ +Hello World diff --git a/runtime/src/main/java/io/micronaut/logging/impl/LogbackLoggingSystem.java b/runtime/src/main/java/io/micronaut/logging/impl/LogbackLoggingSystem.java index bb7c0cc3ad4..f1f98b53106 100644 --- a/runtime/src/main/java/io/micronaut/logging/impl/LogbackLoggingSystem.java +++ b/runtime/src/main/java/io/micronaut/logging/impl/LogbackLoggingSystem.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 original authors + * Copyright 2017-2023 original authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import io.micronaut.core.annotation.Nullable; import io.micronaut.logging.LogLevel; import io.micronaut.logging.LoggingSystem; +import jakarta.inject.Inject; import jakarta.inject.Singleton; import org.slf4j.LoggerFactory; @@ -41,8 +42,35 @@ public final class LogbackLoggingSystem implements LoggingSystem { private final String logbackXmlLocation; + /** + * @deprecated Use {@link LogbackLoggingSystem#LogbackLoggingSystem(String, String)} instead + * @param logbackXmlLocation + */ + @Deprecated public LogbackLoggingSystem(@Nullable @Property(name = "logger.config") String logbackXmlLocation) { - this.logbackXmlLocation = logbackXmlLocation != null ? logbackXmlLocation : DEFAULT_LOGBACK_LOCATION; + this( + System.getProperty("logback.configurationFile"), + logbackXmlLocation + ); + } + + /** + * @param logbackExternalConfigLocation The location of the logback configuration file set via logback properties + * @param logbackXmlLocation The location of the logback configuration file set via micronaut properties + * @since 3.8.8 + */ + @Inject + public LogbackLoggingSystem( + @Nullable @Property(name = "logback.configurationFile") String logbackExternalConfigLocation, + @Nullable @Property(name = "logger.config") String logbackXmlLocation + ) { + if (logbackExternalConfigLocation != null) { + this.logbackXmlLocation = logbackExternalConfigLocation; + } else if (logbackXmlLocation != null) { + this.logbackXmlLocation = logbackXmlLocation; + } else { + this.logbackXmlLocation = DEFAULT_LOGBACK_LOCATION; + } } @Override diff --git a/runtime/src/main/java/io/micronaut/logging/impl/LogbackUtils.java b/runtime/src/main/java/io/micronaut/logging/impl/LogbackUtils.java index 29bb45f652f..a395313c558 100644 --- a/runtime/src/main/java/io/micronaut/logging/impl/LogbackUtils.java +++ b/runtime/src/main/java/io/micronaut/logging/impl/LogbackUtils.java @@ -24,6 +24,8 @@ import io.micronaut.core.annotation.Nullable; import io.micronaut.logging.LoggingSystemException; +import java.io.File; +import java.net.MalformedURLException; import java.net.URL; import java.util.Iterator; import java.util.ServiceLoader; @@ -37,6 +39,8 @@ */ public final class LogbackUtils { + private static final String DEFAULT_LOGBACK_13_PROGRAMMATIC_CONFIGURATOR = "ch.qos.logback.classic.util.DefaultJoranConfigurator"; + private LogbackUtils() { } @@ -50,18 +54,35 @@ private LogbackUtils() { public static void configure(@NonNull ClassLoader classLoader, @NonNull LoggerContext context, @NonNull String logbackXmlLocation) { - configure(context, logbackXmlLocation, () -> classLoader.getResource(logbackXmlLocation)); + configure(context, logbackXmlLocation, () -> { + // Check classpath first + URL resource = classLoader.getResource(logbackXmlLocation); + if (resource != null) { + return resource; + } + // Check file system + File file = new File(logbackXmlLocation); + if (file.exists()) { + try { + resource = file.toURI().toURL(); + } catch (MalformedURLException e) { + + throw new LoggingSystemException("Error creating URL for off-classpath resource", e); + } + } + return resource; + }); } /** * Configures a Logger Context. - * + *

* Searches fpr a custom {@link Configurator} via a service loader. * If not present it configures the context with the resource. * - * @param context Logger Context + * @param context Logger Context * @param logbackXmlLocation the location of the xml logback config file - * @param resourceSupplier A resource for example logback.xml + * @param resourceSupplier A resource for example logback.xml */ private static void configure( @NonNull LoggerContext context, @@ -69,7 +90,7 @@ private static void configure( Supplier resourceSupplier ) { Configurator configurator = loadFromServiceLoader(); - if (configurator != null) { + if (isSupportedConfigurator(context, configurator)) { context.getStatusManager().add(new InfoStatus("Using " + configurator.getClass().getName(), context)); programmaticConfiguration(context, configurator); } else { @@ -86,6 +107,17 @@ private static void configure( } } + private static boolean isSupportedConfigurator(LoggerContext context, Configurator configurator) { + if (configurator == null) { + return false; + } + if (DEFAULT_LOGBACK_13_PROGRAMMATIC_CONFIGURATOR.equals(configurator.getClass().getName())) { + context.getStatusManager().add(new InfoStatus("Skipping " + configurator.getClass().getName() + " as it's assumed to be from an unsupported version of Logback", context)); + return false; + } + return true; + } + /** * Taken from {@link ch.qos.logback.classic.util.ContextInitializer#autoConfig}. */ diff --git a/settings.gradle b/settings.gradle index 1eebe9ed7b6..fd8adac26dd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -70,6 +70,8 @@ include "test-suite-graal" include "test-suite-groovy" include "test-suite-groovy" include "test-suite-logback" +include "test-suite-logback-14" +include "test-suite-logback-external-configuration" include "test-utils" // benchmarks diff --git a/test-suite-logback-14/build.gradle b/test-suite-logback-14/build.gradle new file mode 100644 index 00000000000..e0a46cc5dee --- /dev/null +++ b/test-suite-logback-14/build.gradle @@ -0,0 +1,26 @@ +plugins { + id("io.micronaut.build.internal.convention-test-library") +} + +description = "logback tests with a new version of logback than we support, just to check it runs. Can be removed when we upgrade as part of 4.0.0" + +// Logback 1.4.x is Java 11+ compatible +def logbackVersion = JavaVersion.current().isJava11Compatible() ? "1.4.6" : "1.3.6" + +dependencies { + testAnnotationProcessor(projects.injectJava) + + testImplementation(libs.managed.micronaut.test.spock) { + exclude(group: "io.micronaut", module: "micronaut-aop") + } + testImplementation(projects.context) + testImplementation(projects.injectGroovy) + + // Use a newer version of logback than we support + testImplementation("ch.qos.logback:logback-classic:${logbackVersion}") + + testImplementation(projects.management) + testImplementation(projects.httpClient) + + testRuntimeOnly(projects.httpServerNetty) +} diff --git a/test-suite-logback-14/src/test/groovy/io/micronaut/logback/LoggerConfigurationSpec.groovy b/test-suite-logback-14/src/test/groovy/io/micronaut/logback/LoggerConfigurationSpec.groovy new file mode 100644 index 00000000000..7e79856d9ea --- /dev/null +++ b/test-suite-logback-14/src/test/groovy/io/micronaut/logback/LoggerConfigurationSpec.groovy @@ -0,0 +1,31 @@ +package io.micronaut.logback + +import ch.qos.logback.classic.Level +import ch.qos.logback.classic.Logger +import io.micronaut.context.annotation.Property +import io.micronaut.http.client.HttpClient +import io.micronaut.http.client.annotation.Client +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import jakarta.inject.Inject +import org.slf4j.LoggerFactory +import spock.lang.Specification + +@MicronautTest +// Setting a level in a property forces a refresh, so the XML configuration is ignored. Without this in 3.8.x, the test fails. +@Property(name = "logger.levels.set.by.property", value = "DEBUG") +class LoggerConfigurationSpec extends Specification { + + @Inject + @Client("/") + HttpClient client + + void "if configuration is supplied, xml should be ignored"() { + given: + Logger fromXml = (Logger) LoggerFactory.getLogger("xml.config") + Logger fromProperties = (Logger) LoggerFactory.getLogger("set.by.property") + + expect: + fromXml.level == Level.TRACE + fromProperties.level == Level.DEBUG + } +} diff --git a/test-suite-logback-14/src/test/groovy/io/micronaut/logback/LoggerEndpointSpec.groovy b/test-suite-logback-14/src/test/groovy/io/micronaut/logback/LoggerEndpointSpec.groovy new file mode 100644 index 00000000000..d7aef0db31d --- /dev/null +++ b/test-suite-logback-14/src/test/groovy/io/micronaut/logback/LoggerEndpointSpec.groovy @@ -0,0 +1,71 @@ +package io.micronaut.logback + +import ch.qos.logback.classic.Logger +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.AppenderBase +import io.micronaut.context.annotation.Property +import io.micronaut.http.HttpRequest +import io.micronaut.http.MediaType +import io.micronaut.http.client.HttpClient +import io.micronaut.http.client.annotation.Client +import io.micronaut.logback.controllers.HelloWorldController +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import jakarta.inject.Inject +import org.slf4j.LoggerFactory +import spock.lang.Issue +import spock.lang.Specification + +@MicronautTest +@Property(name = "logger.levels.io.micronaut.logback", value = "INFO") +@Property(name = "endpoints.loggers.enabled", value = "true") +@Property(name = "endpoints.loggers.sensitive", value = "false") +@Property(name = "endpoints.loggers.write-sensitive", value = "false") +@Issue("https://github.com/micronaut-projects/micronaut-core/issues/8679") +class LoggerEndpointSpec extends Specification { + + @Inject + @Client("/") + HttpClient client + + void "logback configuration from properties is as expected"() { + when: + def response = client.toBlocking().retrieve("/loggers/io.micronaut.logback") + + then: + response.contains("INFO") + } + + void "logback can be configured"() { + given: + MemoryAppender appender = new MemoryAppender() + Logger l = (Logger) LoggerFactory.getLogger("io.micronaut.logback.controllers") + l.addAppender(appender) + appender.start() + + when: + def response = client.toBlocking().retrieve("/", String) + + then: 'response is as expected' + response == HelloWorldController.RESPONSE + + and: 'no log message is emitted' + appender.events.empty + + when: 'log level is changed to TRACE' + def body = '{ "configuredLevel": "TRACE" }' + def post = HttpRequest.POST("/loggers/io.micronaut.logback.controllers", body).contentType(MediaType.APPLICATION_JSON_TYPE) + client.toBlocking().exchange(post) + + and: + response = client.toBlocking().retrieve("/", String) + + then: 'response is as expected' + response == HelloWorldController.RESPONSE + + and: 'log message is emitted' + appender.events == [HelloWorldController.LOG_MESSAGE] + + cleanup: + appender.stop() + } +} diff --git a/test-suite-logback-14/src/test/groovy/io/micronaut/logback/LoggerLevelSpec.groovy b/test-suite-logback-14/src/test/groovy/io/micronaut/logback/LoggerLevelSpec.groovy new file mode 100644 index 00000000000..b953c4869e0 --- /dev/null +++ b/test-suite-logback-14/src/test/groovy/io/micronaut/logback/LoggerLevelSpec.groovy @@ -0,0 +1,46 @@ +package io.micronaut.logback + +import ch.qos.logback.classic.Logger +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.AppenderBase +import io.micronaut.context.annotation.Property +import io.micronaut.http.HttpRequest +import io.micronaut.http.MediaType +import io.micronaut.http.client.HttpClient +import io.micronaut.http.client.annotation.Client +import io.micronaut.logback.controllers.HelloWorldController +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import jakarta.inject.Inject +import org.slf4j.LoggerFactory +import spock.lang.Issue +import spock.lang.Specification + +@MicronautTest +@Property(name = "logger.levels.io.micronaut.logback.controllers", value = "TRACE") +@Issue("https://github.com/micronaut-projects/micronaut-core/issues/8678") +class LoggerLevelSpec extends Specification { + + @Inject + @Client("/") + HttpClient client + + void "logback can be configured via properties"() { + given: + MemoryAppender appender = new MemoryAppender() + Logger l = (Logger) LoggerFactory.getLogger("io.micronaut.logback.controllers") + l.addAppender(appender) + appender.start() + + when: + def response = client.toBlocking().retrieve("/", String) + + then: 'response is as expected' + response == HelloWorldController.RESPONSE + + and: 'log message is emitted' + appender.events == [HelloWorldController.LOG_MESSAGE] + + cleanup: + appender.stop() + } +} diff --git a/test-suite-logback-14/src/test/groovy/io/micronaut/logback/MemoryAppender.groovy b/test-suite-logback-14/src/test/groovy/io/micronaut/logback/MemoryAppender.groovy new file mode 100644 index 00000000000..3e978a6888e --- /dev/null +++ b/test-suite-logback-14/src/test/groovy/io/micronaut/logback/MemoryAppender.groovy @@ -0,0 +1,17 @@ +package io.micronaut.logback + +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.AppenderBase +import groovy.transform.CompileStatic +import groovy.transform.PackageScope + +@PackageScope +@CompileStatic +class MemoryAppender extends AppenderBase { + List events = [] + + @Override + protected void append(ILoggingEvent e) { + events << e.formattedMessage + } +} diff --git a/test-suite-logback-14/src/test/java/io/micronaut/logback/Application.java b/test-suite-logback-14/src/test/java/io/micronaut/logback/Application.java new file mode 100644 index 00000000000..8f06535b06c --- /dev/null +++ b/test-suite-logback-14/src/test/java/io/micronaut/logback/Application.java @@ -0,0 +1,15 @@ +package io.micronaut.logback; + +import io.micronaut.runtime.Micronaut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Application { + + private static final Logger LOG = LoggerFactory.getLogger(Application.class); + + public static void main(String[] args) { + LOG.trace("starting the app"); + Micronaut.run(Application.class, args); + } +} diff --git a/test-suite-logback-14/src/test/java/io/micronaut/logback/controllers/HelloWorldController.java b/test-suite-logback-14/src/test/java/io/micronaut/logback/controllers/HelloWorldController.java new file mode 100644 index 00000000000..d431f7bb72b --- /dev/null +++ b/test-suite-logback-14/src/test/java/io/micronaut/logback/controllers/HelloWorldController.java @@ -0,0 +1,21 @@ +package io.micronaut.logback.controllers; + +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Controller +public class HelloWorldController { + + public static final String RESPONSE = "Hello world!"; + public static final String LOG_MESSAGE = "inside hello world"; + + private static final Logger LOG = LoggerFactory.getLogger(HelloWorldController.class); + + @Get + String index() { + LOG.trace(LOG_MESSAGE); + return RESPONSE; + } +} diff --git a/test-suite-logback-14/src/test/resources/logback.xml b/test-suite-logback-14/src/test/resources/logback.xml new file mode 100644 index 00000000000..be7932b2810 --- /dev/null +++ b/test-suite-logback-14/src/test/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + diff --git a/test-suite-logback-external-configuration/build.gradle.kts b/test-suite-logback-external-configuration/build.gradle.kts new file mode 100644 index 00000000000..a35d4e5419d --- /dev/null +++ b/test-suite-logback-external-configuration/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("io.micronaut.build.internal.convention-test-library") +} + +dependencies { + testAnnotationProcessor(projects.injectJava) + + testImplementation(libs.managed.micronaut.test.spock) { + exclude(group="io.micronaut", module="micronaut-aop") + } + testImplementation(projects.context) + testImplementation(projects.injectGroovy) + testImplementation(libs.managed.logback) + testImplementation(projects.management) + testImplementation(projects.httpClient) + + testRuntimeOnly(projects.httpServerNetty) +} diff --git a/test-suite-logback-external-configuration/src/external/external-logback.xml b/test-suite-logback-external-configuration/src/external/external-logback.xml new file mode 100644 index 00000000000..a21b01a7a8d --- /dev/null +++ b/test-suite-logback-external-configuration/src/external/external-logback.xml @@ -0,0 +1,16 @@ + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + diff --git a/test-suite-logback-external-configuration/src/test/groovy/io/micronaut/logback/ExternalConfigurationSpec.groovy b/test-suite-logback-external-configuration/src/test/groovy/io/micronaut/logback/ExternalConfigurationSpec.groovy new file mode 100644 index 00000000000..3a0f4fed2dc --- /dev/null +++ b/test-suite-logback-external-configuration/src/test/groovy/io/micronaut/logback/ExternalConfigurationSpec.groovy @@ -0,0 +1,56 @@ +package io.micronaut.logback + +import ch.qos.logback.classic.Level +import ch.qos.logback.classic.Logger +import io.micronaut.context.ApplicationContext +import io.micronaut.runtime.server.EmbeddedServer +import org.slf4j.LoggerFactory +import spock.lang.See +import spock.lang.Specification +import spock.util.environment.RestoreSystemProperties + +@See("https://logback.qos.ch/manual/configuration.html#auto_configuration") +class ExternalConfigurationSpec extends Specification { + + @RestoreSystemProperties + def "should use the external configuration"() { + given: + System.setProperty("logback.configurationFile", "src/external/external-logback.xml") + + when: + Logger fromXml = (Logger) LoggerFactory.getLogger("i.should.not.exist") + Logger external = (Logger) LoggerFactory.getLogger("external.logging") + + then: 'logback.xml is ignored as we have set a configurationFile' + fromXml.level == null + + and: 'external configuration is used' + external.level == Level.TRACE + } + + @RestoreSystemProperties + def "should still use the external config if custom levels are defines"() { + given: + System.setProperty("logback.configurationFile", "src/external/external-logback.xml") + + when: + def server = ApplicationContext.run(EmbeddedServer, [ + "logger.levels.app.customisation": "DEBUG" + ]) + Logger fromXml = (Logger) LoggerFactory.getLogger("i.should.not.exist") + Logger custom = (Logger) LoggerFactory.getLogger("app.customisation") + Logger external = (Logger) LoggerFactory.getLogger("external.logging") + + then: 'logback.xml is ignored as we have set a configurationFile' + fromXml.level == null + + and: 'custom levels are still respected' + custom.level == Level.DEBUG + + and: 'external configuration is used' + external.level == Level.TRACE + + cleanup: + server.stop() + } +} diff --git a/test-suite-logback-external-configuration/src/test/resources/logback.xml b/test-suite-logback-external-configuration/src/test/resources/logback.xml new file mode 100644 index 00000000000..7b053ed9a39 --- /dev/null +++ b/test-suite-logback-external-configuration/src/test/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + +