Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow setting log level through environment variables #4755

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions runtime/build.gradle
Expand Up @@ -43,6 +43,7 @@ dependencies {
if (!JavaVersion.current().isJava9Compatible()) {
testImplementation files(org.gradle.internal.jvm.Jvm.current().toolsJar)
}
testImplementation "com.github.stefanbirkner:system-lambda:1.1.0"
}

//compileJava.options.fork = true
Expand Down
Expand Up @@ -29,7 +29,9 @@
import org.slf4j.LoggerFactory;

import javax.inject.Singleton;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Properties logging levels configurer.
Expand Down Expand Up @@ -78,18 +80,24 @@ public void onApplicationEvent(RefreshEvent event) {
}

private void configureLogLevels() {
environment.getProperties(LOGGER_LEVELS_PROPERTY_PREFIX, StringConvention.RAW).forEach((loggerPrefix, levelString) -> {
LogLevel newLevel = toLogLevel(levelString.toString());
if (newLevel == null) {
throw new ConfigurationException("Invalid log level: '" + levelString + "' for logger: '" + loggerPrefix + "'");
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Setting log level '{}' for logger: '{}'", newLevel, loggerPrefix);
}
for (LoggingSystem loggingSystem : loggingSystems) {
loggingSystem.setLogLevel(loggerPrefix, newLevel);
}
});
Map<String, Object> properties = new HashMap<>(environment.getProperties(LOGGER_LEVELS_PROPERTY_PREFIX));
// Using raw keys here allows configuring log levels for camelCase package names in application.yml
properties.putAll(environment.getProperties(LOGGER_LEVELS_PROPERTY_PREFIX, StringConvention.RAW));
properties.forEach(this::configureLogLevelForPrefix);
}

private void configureLogLevelForPrefix(String loggerPrefix, Object levelValue) {
LogLevel newLevel = toLogLevel(levelValue.toString());
if (newLevel == null) {
throw new ConfigurationException("Invalid log level: '" + levelValue + "' for logger: '" + loggerPrefix + "'");
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Setting log level '{}' for logger: '{}'", newLevel, loggerPrefix);
}
LOGGER.info("Setting log level '{}' for logger: '{}'", newLevel, loggerPrefix);
for (LoggingSystem loggingSystem : loggingSystems) {
loggingSystem.setLogLevel(loggerPrefix, newLevel);
}
}

private static LogLevel toLogLevel(String logLevel) {
Expand Down
Expand Up @@ -24,7 +24,6 @@
import io.micronaut.context.scope.CustomScope;
import io.micronaut.core.convert.value.MutableConvertibleValues;
import io.micronaut.core.util.ArgumentUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.context.ServerRequestContext;
import io.micronaut.http.context.event.HttpRequestTerminatedEvent;
Expand Down
Expand Up @@ -2,6 +2,7 @@ package io.micronaut.logging

import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import com.github.stefanbirkner.systemlambda.SystemLambda
import io.micronaut.context.ApplicationContext
import org.slf4j.LoggerFactory
import spock.lang.Specification
Expand All @@ -15,13 +16,15 @@ class LogbackLogLevelConfigurerSpec extends Specification {
((Logger) LoggerFactory.getLogger('foo.bar1')).setLevel(Level.DEBUG)
((Logger) LoggerFactory.getLogger('foo.bar2')).setLevel(Level.DEBUG)
((Logger) LoggerFactory.getLogger('foo.bar3')).setLevel(Level.ERROR)
((Logger) LoggerFactory.getLogger('foo.barBaz')).setLevel(Level.WARN)

when:
ApplicationContext context = ApplicationContext.run(
[
'logger.levels.aaa.bbb.ccc': 'ERROR',
'logger.levels.foo.bar2' : 'INFO',
'logger.levels.foo.bar3' : '',
'logger.levels.foo.barBaz' : 'INFO',
]
)

Expand All @@ -37,6 +40,32 @@ class LogbackLogLevelConfigurerSpec extends Specification {
'foo.bar2' | Level.INFO
'foo.bar3' | null
'aaa.bbb.ccc' | Level.ERROR
'foo.barBaz' | Level.INFO

}

void 'test that log levels can be configured via environment variables'() {
given:
((Logger) LoggerFactory.getLogger('foo.bar1')).setLevel(Level.DEBUG)
((Logger) LoggerFactory.getLogger('foo.bar2')).setLevel(Level.DEBUG)

when:
ApplicationContext context = ApplicationContext.builder().build()
SystemLambda.withEnvironmentVariable("LOGGER_LEVELS_FOO_BAR2", "INFO")
.execute(() -> {
context.start()
})

then:
((Logger) LoggerFactory.getLogger(loggerName)).getLevel() == expectedLevel

cleanup:
context.close()

where:
loggerName | expectedLevel
'foo.bar1' | Level.DEBUG
'foo.bar2' | Level.INFO

}

Expand Down
2 changes: 2 additions & 0 deletions src/main/docs/guide/config/propertySource.adoc
Expand Up @@ -177,4 +177,6 @@ logger:
foo.bar: ERROR
----

The same configuration can be achieved by setting the environment variable `LOGGER_LEVELS_FOO_BAR`. Note that there is currently no way to set log levels for unconventional prefixes such as `foo.barBaz`.

Note that the ability to control log levels via config is controlled via the api:logging.LoggingSystem[] interface. Currently Micronaut ships with a single implementation that allows setting log levels for the Logback library. If another library is chosen you should provide a bean that implements this interface.