public class LogbackHelper {
private static final String ALL_LOGS_TO_CONSOLE_PROPERTY = "sonar.log.console";
+ private static final String PROCESS_NAME_PLACEHOLDER = "XXXX";
+ private static final String THREAD_ID_PLACEHOLDER = "ZZZZ";
private static final String SONAR_LOG_LEVEL_PROPERTY = "sonar.log.level";
+ private static final String SONAR_PROCESS_LOG_LEVEL_PROPERTY = "sonar.log.level." + PROCESS_NAME_PLACEHOLDER;
private static final String ROLLING_POLICY_PROPERTY = "sonar.log.rollingPolicy";
private static final String MAX_FILES_PROPERTY = "sonar.log.maxFiles";
- private static final String PROCESS_NAME_PLACEHOLDER = "XXXX";
- private static final String THREAD_ID_PLACEHOLDER = "ZZZZ";
private static final String LOG_FORMAT = "%d{yyyy.MM.dd HH:mm:ss} %-5level " + PROCESS_NAME_PLACEHOLDER + "[" + THREAD_ID_PLACEHOLDER + "][%logger{20}] %msg%n";
public static final Set<Level> ALLOWED_ROOT_LOG_LEVELS = unmodifiableSet(setOf(Level.TRACE, Level.DEBUG, Level.INFO));
public String buildLogPattern(LogbackHelper.RootLoggerConfig config) {
return LOG_FORMAT
- .replace(PROCESS_NAME_PLACEHOLDER, config.getProcessName())
- .replace(THREAD_ID_PLACEHOLDER, config.getThreadIdFieldPattern());
+ .replace(PROCESS_NAME_PLACEHOLDER, config.getProcessName())
+ .replace(THREAD_ID_PLACEHOLDER, config.getThreadIdFieldPattern());
}
/**
/**
* Make logback configuration for a process to push all its logs to a log file.
* <p>
- * <ul>
- * <li>the file's name will use the prefix defined in {@link RootLoggerConfig#getFileNamePrefix()}.</li>
- * <li>the file will follow the rotation policy defined in property {@link #ROLLING_POLICY_PROPERTY} and
- * the max number of files defined in property {@link #MAX_FILES_PROPERTY}</li>
- * <li>the logs will follow the specified log pattern</li>
- * </ul>
+ * <ul>
+ * <li>the file's name will use the prefix defined in {@link RootLoggerConfig#getFileNamePrefix()}.</li>
+ * <li>the file will follow the rotation policy defined in property {@link #ROLLING_POLICY_PROPERTY} and
+ * the max number of files defined in property {@link #MAX_FILES_PROPERTY}</li>
+ * <li>the logs will follow the specified log pattern</li>
+ * </ul>
* </p>
*
* @see #buildLogPattern(RootLoggerConfig)
*
* @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) {
- return configureRootLogLevel(props, SONAR_LOG_LEVEL_PROPERTY);
+ 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()));
}
/**
- * Configure the log level of the root logger reading the value of specified property.
+ * Configure the log level of the root logger reading the value of specified properties. The lowest level will be
+ * applied.
*
* @throws IllegalArgumentException if the value of the specified property is not one of {@link #ALLOWED_ROOT_LOG_LEVELS}
*/
- public Level configureRootLogLevel(Props props, String propertyKey) {
- Level newLevel = Level.toLevel(props.value(propertyKey, Level.INFO.toString()), Level.INFO);
+ public Level configureRootLogLevel(Props props, String... propertyKeys) {
+ Level newLevel = Level.INFO;
+ for (String propertyKey : propertyKeys) {
+ Level level = Level.toLevel(props.value(propertyKey, Level.INFO.toString()), Level.INFO);
+ if (!level.isGreaterOrEqual(newLevel)) {
+ newLevel = level;
+ }
+ }
return configureRootLogLevel(newLevel);
}
String logPattern = helper.buildLogPattern(config);
helper.configureGlobalFileLog(props, config, logPattern);
helper.configureForSubprocessGobbler(props, logPattern);
- helper.configureRootLogLevel(props);
+ // SQ's global log level must not change ES's log level
+ helper.configureRootLogLevel(props, "sonar.log.es.level");
return ctx;
}
*/
package org.sonar.ce.log;
+import org.sonar.process.ProcessId;
import org.sonar.process.Props;
import org.sonar.server.app.ServerProcessLogging;
+import static org.sonar.ce.log.CeLogging.MDC_CE_TASK_UUID;
+
/**
* Configure logback for the Compute Engine process. Logs are written to file "ce.log" in SQ's log directory.
*/
public class CeProcessLogging extends ServerProcessLogging {
public CeProcessLogging() {
- super("ce", "%X{ceTaskUuid}");
+ super(ProcessId.COMPUTE_ENGINE, "%X{" + MDC_CE_TASK_UUID + "}");
}
@Override
import ch.qos.logback.classic.LoggerContext;
import org.sonar.process.LogbackHelper;
+import org.sonar.process.ProcessId;
import org.sonar.process.Props;
import org.sonar.server.platform.ServerLogging;
import static org.sonar.process.LogbackHelper.RootLoggerConfig.newRootLoggerConfigBuilder;
public abstract class ServerProcessLogging {
- private final String processName;
+ private final ProcessId processId;
private final String threadIdFieldPattern;
private final LogbackHelper helper = new LogbackHelper();
- protected ServerProcessLogging(String processName, String threadIdFieldPattern) {
- this.processName = processName;
+ protected ServerProcessLogging(ProcessId processId, String threadIdFieldPattern) {
+ this.processId = processId;
this.threadIdFieldPattern = threadIdFieldPattern;
}
private void configureRootLogger(Props props) {
LogbackHelper.RootLoggerConfig config = newRootLoggerConfigBuilder()
- .setProcessName(processName)
+ .setProcessName(processId.getKey())
.setThreadIdFieldPattern(threadIdFieldPattern)
- .setFileNamePrefix(processName)
+ .setFileNamePrefix(processId.getKey())
.build();
String logPattern = helper.buildLogPattern(config);
helper.configureGlobalFileLog(props, config, logPattern);
helper.configureForSubprocessGobbler(props, logPattern);
- helper.configureRootLogLevel(props);
+ helper.configureRootLogLevel(props, processId);
ServerLogging.configureHardcodedLevels(helper);
}
import java.util.logging.LogManager;
import org.slf4j.bridge.SLF4JBridgeHandler;
+import org.sonar.process.ProcessId;
import org.sonar.process.Props;
/**
public class WebServerProcessLogging extends ServerProcessLogging {
public WebServerProcessLogging() {
- super("web", "%X{UID}");
+ super(ProcessId.WEB_SERVER, "%X{UID}");
}
@Override
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.FileAppender;
import org.sonar.process.LogbackHelper;
+import org.sonar.process.ProcessId;
import org.sonar.process.Props;
import static org.slf4j.Logger.ROOT_LOGGER_NAME;
} else {
configureWithWrapperWritingToFile(ctx);
}
- helper.configureRootLogLevel(props, "sonar.app.log.level");
+ helper.configureRootLogLevel(props, ProcessId.APP);
return ctx;
}
@Test
public void root_logger_level_can_be_changed_with_a_property() {
- props.set("sonar.app.log.level", "TRACE");
+ props.set("sonar.log.level.app", "TRACE");
LoggerContext ctx = underTest.configure(props);
Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME);
@Test
public void property_changing_root_logger_level_is_case_insensitive() {
- props.set("sonar.app.log.level", "trace");
+ props.set("sonar.log.level.app", "trace");
LoggerContext ctx = underTest.configure(props);
Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME);
@Test
public void default_to_INFO_if_property_changing_root_logger_level_has_invalid_value() {
- props.set("sonar.app.log.level", "DodoDouh!");
+ props.set("sonar.log.level.app", "DodoDouh!");
LoggerContext ctx = underTest.configure(props);
Logger rootLogger = ctx.getLogger(ROOT_LOGGER_NAME);