import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashSet;
import java.util.Set;
import javax.annotation.CheckForNull;
import org.apache.commons.lang.StringUtils;
return new Builder();
}
- public ProcessId getProcessId() {
+ ProcessId getProcessId() {
return processId;
}
public static final class Builder {
@CheckForNull
- public ProcessId processId;
+ private ProcessId processId;
private String threadIdFieldPattern = "";
private Builder() {
return props.valueAsBoolean(ALL_LOGS_TO_CONSOLE_PROPERTY, false);
}
- @SafeVarargs
- private static <T> Set<T> setOf(T... args) {
- Set<T> res = new HashSet<>(args.length);
- res.addAll(Arrays.asList(args));
- return res;
- }
-
/**
* Configure the log level of the root logger reading the value of property {@link #SONAR_LOG_LEVEL_PROPERTY}.
*
* @throws IllegalArgumentException if the value of {@link #SONAR_LOG_LEVEL_PROPERTY} is not one of {@link #ALLOWED_ROOT_LOG_LEVELS}
*/
public Level configureRootLogLevel(Props props, ProcessId processId) {
- return configureRootLogLevel(props,
- SONAR_LOG_LEVEL_PROPERTY,
- SONAR_PROCESS_LOG_LEVEL_PROPERTY.replace(PROCESS_NAME_PLACEHOLDER, processId.getKey()));
+ Level newLevel = resolveLevel(props, SONAR_LOG_LEVEL_PROPERTY, SONAR_PROCESS_LOG_LEVEL_PROPERTY.replace(PROCESS_NAME_PLACEHOLDER, processId.getKey()));
+ getRootContext().getLogger(ROOT_LOGGER_NAME).setLevel(newLevel);
+ return newLevel;
}
/**
- * Configure the log level of the root logger reading the value of specified properties.
+ * Resolve a log level reading the value of specified properties.
* <p>
* To compute the applied log level the following rules will be followed:
* <ul>the last property with a defined and valid value in the order of the {@code propertyKeys} argument will be applied</ul>
- * <ul>if there is none, {@link Level#INFO INFO} will apply</ul>
+ * <ul>if there is none, {@link Level#INFO INFO} will be returned</ul>
* </p>
*
* @throws IllegalArgumentException if the value of the specified property is not one of {@link #ALLOWED_ROOT_LOG_LEVELS}
*/
- public Level configureRootLogLevel(Props props, String... propertyKeys) {
- Level newLevel = resolveLevel(props, propertyKeys);
- getRootContext().getLogger(ROOT_LOGGER_NAME).setLevel(newLevel);
- return newLevel;
- }
-
private static Level resolveLevel(Props props, String... propertyKeys) {
Level newLevel = Level.INFO;
for (String propertyKey : propertyKeys) {
String logPattern = helper.buildLogPattern(config);
helper.configureGlobalFileLog(props, config, logPattern);
helper.configureForSubprocessGobbler(props, logPattern);
- // SQ's global log level must not change ES's log level
- helper.configureRootLogLevel(props, "sonar.log.level.es");
+ helper.configureRootLogLevel(props, ProcessId.ELASTICSEARCH);
return ctx;
}
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sonar.process.LogbackHelper;
import org.sonar.process.ProcessProperties;
public class SearchLoggingTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
private File logDir;
}
@Test
- public void root_log_level_does_not_change_with_global_property_value() {
+ public void default_level_for_root_logger_is_INFO() {
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyRootLogLevel(ctx, Level.INFO);
+ }
+
+ @Test
+ public void root_logger_level_changes_with_global_property() {
props.set("sonar.log.level", "TRACE");
LoggerContext ctx = underTest.configure(props);
- Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME);
- assertThat(rootLogger.getLevel()).isEqualTo(Level.INFO);
+ verifyRootLogLevel(ctx, Level.TRACE);
}
@Test
- public void root_log_level_change_with_es_property_value() {
+ public void root_logger_level_changes_with_es_property() {
props.set("sonar.log.level.es", "TRACE");
LoggerContext ctx = underTest.configure(props);
+ verifyRootLogLevel(ctx, Level.TRACE);
+ }
+
+ @Test
+ public void root_logger_level_is_configured_from_es_property_over_global_property() {
+ props.set("sonar.log.level", "TRACE");
+ props.set("sonar.log.level.es", "DEBUG");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyRootLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void root_logger_level_changes_with_es_property_and_is_case_insensitive() {
+ props.set("sonar.log.level.es", "deBug");
+
+ LoggerContext ctx = underTest.configure(props);
+
+ verifyRootLogLevel(ctx, Level.DEBUG);
+ }
+
+ @Test
+ public void root_logger_level_defaults_to_INFO_if_es_property_has_invalid_value() {
+ props.set("sonar.log.level.es", "DodoDouh!");
+
+ LoggerContext ctx = underTest.configure(props);
+ verifyRootLogLevel(ctx, Level.INFO);
+ }
+
+ @Test
+ public void fail_with_IAE_if_global_property_unsupported_level() {
+ props.set("sonar.log.level", "ERROR");
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("log level ERROR in property sonar.log.level is not a supported value (allowed levels are [TRACE, DEBUG, INFO])");
+
+ underTest.configure(props);
+ }
+
+ @Test
+ public void fail_with_IAE_if_es_property_unsupported_level() {
+ props.set("sonar.log.level.es", "ERROR");
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("log level ERROR in property sonar.log.level.es is not a supported value (allowed levels are [TRACE, DEBUG, INFO])");
+
+ underTest.configure(props);
+ }
+
+ private void verifyRootLogLevel(LoggerContext ctx, Level expected) {
Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME);
- assertThat(rootLogger.getLevel()).isEqualTo(Level.TRACE);
+ assertThat(rootLogger.getLevel()).isEqualTo(expected);
}
}
}
@Test
- public void root_logger_level_changes_with_ce_property_and_is_case_insensitive() {
+ public void root_logger_level_changes_with_web_property_and_is_case_insensitive() {
props.set("sonar.log.level.web", "debug");
LoggerContext ctx = underTest.configure(props);
# Some logs, however, will follow the convention to provide data in payload in the format " | key=value"
# Especially, log of profiled pieces of code will end with " | time=XXXXms".
-# Global level of logs (applies to App, Web and CE processes).
+# Global level of logs (applies to all 4 processes).
# Supported values are INFO (default), DEBUG and TRACE
#sonar.log.level=INFO
#sonar.log.maxFiles=7
# Access log is the list of all the HTTP requests received by server. If enabled, it is stored
-# in the file {sonar.path.logs}/access.log. This file follows the same rolling policy as for
-# sonar.log (see sonar.log.rollingPolicy and sonar.log.maxFiles).
+# in the file {sonar.path.logs}/access.log. This file follows the same rolling policy as other log file
+# (see sonar.log.rollingPolicy and sonar.log.maxFiles).
#sonar.web.accessLogs.enable=true
# Format of access log. It is ignored if sonar.web.accessLogs.enable=false. Possible values are: