import org.sonar.process.DefaultProcessCommands;
import org.sonar.process.ProcessId;
+import static org.sonar.server.app.ServerProcessLogging.STARTUP_LOGGER_NAME;
+
/**
* Waits for the web server to be operational (started and datastores up-to-date)
*/
return true;
}
+ Loggers.get(STARTUP_LOGGER_NAME).info("Compute Engine startup is on hold. Waiting for Web Server to be operational.");
LOG.info("Waiting for Web Server to be operational...");
Logger logarithmicLogger = LogarithmicLogger.from(LOG).applyingCallRatio(CALL_RATIO).build();
while (!processCommands.isOperational()) {
package org.sonar.server.app;
import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.ConsoleAppender;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
-import org.sonar.process.logging.LogLevelConfig;
-import org.sonar.process.logging.LogbackHelper;
import org.sonar.process.ProcessId;
import org.sonar.process.Props;
+import org.sonar.process.logging.LogLevelConfig;
+import org.sonar.process.logging.LogbackHelper;
import org.sonar.process.logging.RootLoggerConfig;
import static org.sonar.process.logging.RootLoggerConfig.newRootLoggerConfigBuilder;
public abstract class ServerProcessLogging {
+
+ public static final String STARTUP_LOGGER_NAME = "startup";
protected static final Set<String> JMX_RMI_LOGGER_NAMES = ImmutableSet.of(
"javax.management.remote.timeout",
"javax.management.remote.misc",
ctx.reset();
helper.enableJulChangePropagation(ctx);
+
configureRootLogger(props);
helper.apply(logLevelConfig, props);
+ configureDirectToConsoleLoggers(ctx, STARTUP_LOGGER_NAME);
extendConfigure();
helper.configureForSubprocessGobbler(props, logPattern);
}
+ /**
+ * Setup one or more specified loggers to be non additive and to print to System.out which will be caught by the Main
+ * Process and written to sonar.log.
+ */
+ private void configureDirectToConsoleLoggers(LoggerContext context, String... loggerNames) {
+ RootLoggerConfig config = newRootLoggerConfigBuilder()
+ .setProcessId(ProcessId.APP)
+ .setThreadIdFieldPattern("")
+ .build();
+ String logPattern = helper.buildLogPattern(config);
+ ConsoleAppender<ILoggingEvent> consoleAppender = helper.newConsoleAppender(context, "CONSOLE", logPattern);
+
+ for (String loggerName : loggerNames) {
+ Logger consoleLogger = context.getLogger(loggerName);
+ consoleLogger.setAdditive(false);
+ consoleLogger.addAppender(consoleAppender);
+ }
+ }
+
}
import org.sonar.api.utils.log.Loggers;
import org.sonar.db.version.DatabaseVersion;
+import static org.sonar.server.app.ServerProcessLogging.STARTUP_LOGGER_NAME;
+
public class DatabaseServerCompatibility implements Startable {
+ private static final String HIGHLIGHTER = "################################################################################";
private DatabaseVersion version;
public DatabaseServerCompatibility(DatabaseVersion version) {
if (currentVersion != null && currentVersion < DatabaseVersion.MIN_UPGRADE_VERSION) {
throw MessageException.of("Current version is too old. Please upgrade to Long Term Support version firstly.");
}
- Loggers.get(DatabaseServerCompatibility.class).warn("Database must be upgraded. Please backup database and browse /setup");
+ String msg = "Database must be upgraded. Please backup database and browse /setup";
+ Loggers.get(DatabaseServerCompatibility.class).warn(msg);
+ Loggers.get(STARTUP_LOGGER_NAME).warn('\n'
+ + HIGHLIGHTER + '\n'
+ + " " + msg
+ + '\n' + HIGHLIGHTER);
}
}
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.joran.spi.JoranException;
import java.io.File;
assertThat(appender).isNull();
}
+ @Test
+ public void startup_logger_prints_to_only_to_system_out() {
+ LoggerContext ctx = underTest.configure(props);
+
+ Logger startup = ctx.getLogger("startup");
+ assertThat(startup.isAdditive()).isFalse();
+ Appender appender = startup.getAppender("CONSOLE");
+ assertThat(appender).isInstanceOf(ConsoleAppender.class);
+ ConsoleAppender<ILoggingEvent> consoleAppender = (ConsoleAppender<ILoggingEvent>) appender;
+ assertThat(consoleAppender.getTarget()).isEqualTo("System.out");
+ assertThat(consoleAppender.getEncoder()).isInstanceOf(PatternLayoutEncoder.class);
+ PatternLayoutEncoder patternEncoder = (PatternLayoutEncoder) consoleAppender.getEncoder();
+ assertThat(patternEncoder.getPattern()).isEqualTo("%d{yyyy.MM.dd HH:mm:ss} %-5level app[][%logger{20}] %msg%n");
+ }
+
@Test
public void log_to_ce_file() {
LoggerContext ctx = underTest.configure(props);
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.joran.spi.JoranException;
import java.io.File;
assertThat(appender).isNull();
}
+ @Test
+ public void startup_logger_prints_to_only_to_system_out() {
+ LoggerContext ctx = underTest.configure(props);
+
+ Logger startup = ctx.getLogger("startup");
+ assertThat(startup.isAdditive()).isFalse();
+ Appender appender = startup.getAppender("CONSOLE");
+ assertThat(appender).isInstanceOf(ConsoleAppender.class);
+ ConsoleAppender<ILoggingEvent> consoleAppender = (ConsoleAppender<ILoggingEvent>) appender;
+ assertThat(consoleAppender.getTarget()).isEqualTo("System.out");
+ assertThat(consoleAppender.getEncoder()).isInstanceOf(PatternLayoutEncoder.class);
+ PatternLayoutEncoder patternEncoder = (PatternLayoutEncoder) consoleAppender.getEncoder();
+ assertThat(patternEncoder.getPattern()).isEqualTo("%d{yyyy.MM.dd HH:mm:ss} %-5level app[][%logger{20}] %msg%n");
+ }
+
@Test
public void log_to_web_file() {
LoggerContext ctx = underTest.configure(props);
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.db.version.DatabaseVersion;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@Rule
public ExpectedException thrown = ExpectedException.none();
+ @Rule
+ public LogTester logTester = new LogTester();
@Test
public void fail_if_requires_downgrade() {
when(version.getStatus()).thenReturn(DatabaseVersion.Status.REQUIRES_UPGRADE);
when(version.getVersion()).thenReturn(DatabaseVersion.MIN_UPGRADE_VERSION);
new DatabaseServerCompatibility(version).start();
- // oh well... how to simply test logging ?
- // Let's assume that this test verifies that no error is raised.
+
+ assertThat(logTester.logs()).hasSize(2);
+ assertThat(logTester.logs(LoggerLevel.WARN)).contains(
+ "Database must be upgraded. Please backup database and browse /setup",
+ "\n################################################################################\n" +
+ " Database must be upgraded. Please backup database and browse /setup\n" +
+ "################################################################################");
}
@Test