From 62bded91ea52f42a71ad7d7624d5447b1a101420 Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Tue, 16 Jun 2015 17:37:13 +0200 Subject: [PATCH] SONAR-6648 Allow to redirect logs to custom stream --- .../org/sonar/batch/bootstrapper/Batch.java | 23 ++- .../bootstrapper/LogCallbackAppender.java | 60 ++++++++ .../bootstrapper/LoggingConfiguration.java | 73 +++++---- .../bootstrapper/LoggingConfigurator.java | 77 ++++++++++ .../sonar/batch/bootstrapper/BatchTest.java | 13 +- .../LoggingConfigurationTest.java | 47 +++--- .../bootstrapper/LoggingConfiguratorTest.java | 139 ++++++++++++++++++ .../batch/bootstrapper/PrintStreamTest.java | 102 +++++++++++++ .../java/org/sonar/home/log/LogListener.java | 44 ++---- 9 files changed, 481 insertions(+), 97 deletions(-) create mode 100644 sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LogCallbackAppender.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfigurator.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/bootstrapper/LoggingConfiguratorTest.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/bootstrapper/PrintStreamTest.java rename sonar-plugin-api/src/main/java/org/sonar/api/utils/ProjectTempFolder.java => sonar-home/src/main/java/org/sonar/home/log/LogListener.java (56%) diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java index 70da9601bd1..c82124d64ab 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Batch.java @@ -19,6 +19,9 @@ */ package org.sonar.batch.bootstrapper; +import org.sonar.home.log.LogListener; + +import org.picocontainer.annotations.Nullable; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.sonar.batch.bootstrap.GlobalContainer; @@ -35,7 +38,7 @@ import java.util.Map; public final class Batch { private boolean started = false; - private LoggingConfiguration logging; + private LoggingConfiguration loggingConfig; private List components; private Map bootstrapProperties = Maps.newHashMap(); private GlobalContainer bootstrapContainer; @@ -50,12 +53,16 @@ public final class Batch { bootstrapProperties.putAll(builder.bootstrapProperties); } if (builder.isEnableLoggingConfiguration()) { - logging = LoggingConfiguration.create(builder.environment).setProperties(bootstrapProperties); + loggingConfig = new LoggingConfiguration(builder.environment).setProperties(bootstrapProperties); + + if (builder.listener != null) { + loggingConfig.setListener(builder.listener); + } } } public LoggingConfiguration getLoggingConfiguration() { - return logging; + return loggingConfig; } /** @@ -110,8 +117,8 @@ public final class Batch { } private void configureLogging() { - if (logging != null) { - logging.configure(); + if (loggingConfig != null) { + LoggingConfigurator.apply(loggingConfig); } } @@ -124,6 +131,7 @@ public final class Batch { private EnvironmentInformation environment; private List components = Lists.newArrayList(); private boolean enableLoggingConfiguration = true; + private LogListener listener; private Builder() { } @@ -138,6 +146,11 @@ public final class Batch { return this; } + public Builder setLogListener(@Nullable LogListener listener) { + this.listener = listener; + return this; + } + /** * @deprecated since 3.7 use {@link #setBootstrapProperties(Map)} */ diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LogCallbackAppender.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LogCallbackAppender.java new file mode 100644 index 00000000000..6c216410d13 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LogCallbackAppender.java @@ -0,0 +1,60 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrapper; + +import org.sonar.home.log.LogListener; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.core.UnsynchronizedAppenderBase; +import ch.qos.logback.classic.spi.ILoggingEvent; + +public class LogCallbackAppender extends UnsynchronizedAppenderBase { + protected LogListener target; + + public LogCallbackAppender(LogListener target) { + setTarget(target); + } + + public void setTarget(LogListener target) { + this.target = target; + } + + @Override + protected void append(ILoggingEvent event) { + target.log(event.getFormattedMessage(), translate(event.getLevel())); + } + + private LogListener.Level translate(Level level) { + switch(level.toInt()) { + case Level.ERROR_INT: + return LogListener.Level.ERROR; + case Level.WARN_INT: + return LogListener.Level.WARN; + case Level.INFO_INT: + return LogListener.Level.INFO; + case Level.DEBUG_INT: + return LogListener.Level.DEBUG; + case Level.TRACE_INT: + return LogListener.Level.TRACE; + default: + return LogListener.Level.DEBUG; + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfiguration.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfiguration.java index da83b54becb..e2ee829310d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfiguration.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfiguration.java @@ -19,15 +19,14 @@ */ package org.sonar.batch.bootstrapper; +import org.sonar.home.log.LogListener; + import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Maps; import org.apache.commons.lang.StringUtils; -import org.sonar.core.config.Logback; import javax.annotation.Nullable; -import java.io.File; -import java.io.PrintStream; import java.util.Map; /** @@ -37,18 +36,25 @@ public final class LoggingConfiguration { public static final String PROPERTY_ROOT_LOGGER_LEVEL = "ROOT_LOGGER_LEVEL"; public static final String PROPERTY_SQL_LOGGER_LEVEL = "SQL_LOGGER_LEVEL"; + public static final String PROPERTY_FORMAT = "FORMAT"; - public static final String LEVEL_ROOT_VERBOSE = "DEBUG"; + public static final String LEVEL_ROOT_VERBOSE = "DEBUG"; public static final String LEVEL_ROOT_DEFAULT = "INFO"; + @VisibleForTesting static final String FORMAT_DEFAULT = "%d{HH:mm:ss.SSS} %-5level - %msg%n"; @VisibleForTesting static final String FORMAT_MAVEN = "[%level] [%d{HH:mm:ss.SSS}] %msg%n"; - private Map substitutionVariables = Maps.newHashMap(); + Map substitutionVariables = Maps.newHashMap(); + LogListener listener = null; - private LoggingConfiguration(@Nullable EnvironmentInformation environment) { + public LoggingConfiguration() { + this(null); + } + + public LoggingConfiguration(@Nullable EnvironmentInformation environment) { setVerbose(false); if (environment != null && "maven".equalsIgnoreCase(environment.getKey())) { setFormat(FORMAT_MAVEN); @@ -57,25 +63,14 @@ public final class LoggingConfiguration { } } - static LoggingConfiguration create(@Nullable EnvironmentInformation environment) { - return new LoggingConfiguration(environment); - } - public LoggingConfiguration setProperties(Map properties) { - String logLevel = properties.get("sonar.log.level"); - String deprecatedProfilingLevel = properties.get("sonar.log.profilingLevel"); - boolean verbose = "true".equals(properties.get("sonar.verbose")) || - "DEBUG".equals(logLevel) || "TRACE".equals(logLevel) || - "BASIC".equals(deprecatedProfilingLevel) || "FULL".equals(deprecatedProfilingLevel); - boolean sql = "TRACE".equals(logLevel) || "FULL".equals(deprecatedProfilingLevel); - - setShowSql(sql); - setVerbose(verbose); + setShowSql(properties); + setVerbose(properties); return this; } - - public LoggingConfiguration setStreams(PrintStream out, PrintStream err) { - + + public LoggingConfiguration setListener(@Nullable LogListener listener) { + this.listener = listener; return this; } @@ -83,6 +78,16 @@ public final class LoggingConfiguration { return setRootLevel(verbose ? LEVEL_ROOT_VERBOSE : LEVEL_ROOT_DEFAULT); } + public LoggingConfiguration setVerbose(Map properties) { + String logLevel = properties.get("sonar.log.level"); + String deprecatedProfilingLevel = properties.get("sonar.log.profilingLevel"); + boolean verbose = "true".equals(properties.get("sonar.verbose")) || + "DEBUG".equals(logLevel) || "TRACE".equals(logLevel) || + "BASIC".equals(deprecatedProfilingLevel) || "FULL".equals(deprecatedProfilingLevel); + + return setVerbose(verbose); + } + public LoggingConfiguration setRootLevel(String level) { return addSubstitutionVariable(PROPERTY_ROOT_LOGGER_LEVEL, level); } @@ -91,6 +96,14 @@ public final class LoggingConfiguration { return addSubstitutionVariable(PROPERTY_SQL_LOGGER_LEVEL, showSql ? "TRACE" : "WARN"); } + public LoggingConfiguration setShowSql(Map properties) { + String logLevel = properties.get("sonar.log.level"); + String deprecatedProfilingLevel = properties.get("sonar.log.profilingLevel"); + boolean sql = "TRACE".equals(logLevel) || "FULL".equals(deprecatedProfilingLevel); + + return setShowSql(sql); + } + @VisibleForTesting LoggingConfiguration setFormat(String format) { return addSubstitutionVariable(PROPERTY_FORMAT, StringUtils.defaultIfBlank(format, FORMAT_DEFAULT)); @@ -101,22 +114,8 @@ public final class LoggingConfiguration { return this; } + @VisibleForTesting String getSubstitutionVariable(String key) { return substitutionVariables.get(key); } - - LoggingConfiguration configure(String classloaderPath) { - Logback.configure(classloaderPath, substitutionVariables); - return this; - } - - LoggingConfiguration configure(File logbackFile) { - Logback.configure(logbackFile, substitutionVariables); - return this; - } - - LoggingConfiguration configure() { - Logback.configure("/org/sonar/batch/bootstrapper/logback.xml", substitutionVariables); - return this; - } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfigurator.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfigurator.java new file mode 100644 index 00000000000..9a74eda6da5 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/LoggingConfigurator.java @@ -0,0 +1,77 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrapper; + +import org.sonar.home.log.LogListener; + +import ch.qos.logback.core.Appender; +import ch.qos.logback.classic.Level; +import org.apache.commons.lang.StringUtils; + +import java.io.File; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import org.slf4j.LoggerFactory; +import org.sonar.core.config.Logback; + +public class LoggingConfigurator { + private LoggingConfigurator() { + } + + public static void apply(LoggingConfiguration conf, File logbackFile) { + Logback.configure(logbackFile, conf.substitutionVariables); + + if (conf.listener != null) { + setCustomRootAppender(conf); + } + } + + public static void apply(LoggingConfiguration conf) { + apply(conf, "/org/sonar/batch/bootstrapper/logback.xml"); + } + + public static void apply(LoggingConfiguration conf, String classloaderPath) { + Logback.configure(classloaderPath, conf.substitutionVariables); + + // if not set, keep default behavior (configured to stdout through the file in classpath) + if (conf.listener != null) { + setCustomRootAppender(conf); + } + } + + private static void setCustomRootAppender(LoggingConfiguration conf) { + Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + String pattern = StringUtils.defaultIfBlank(conf.substitutionVariables.get(LoggingConfiguration.PROPERTY_FORMAT), LoggingConfiguration.FORMAT_DEFAULT); + String level = StringUtils.defaultIfBlank(conf.substitutionVariables.get(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL), LoggingConfiguration.LEVEL_ROOT_DEFAULT); + + logger.detachAndStopAllAppenders(); + logger.addAppender(createAppender(pattern, conf.listener)); + logger.setLevel(Level.toLevel(level)); + } + + private static Appender createAppender(String pattern, LogListener target) { + LogCallbackAppender appender = new LogCallbackAppender(target); + appender.setName("custom_stream"); + appender.start(); + + return appender; + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java index 401353c5b83..e13eed530c2 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/BatchTest.java @@ -19,8 +19,12 @@ */ package org.sonar.batch.bootstrapper; -import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import org.sonar.home.log.LogListener; +import org.junit.Test; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -61,4 +65,11 @@ public class BatchTest { public void loggingConfigurationShouldBeEnabledByDefault() { assertNotNull(newBatch().getLoggingConfiguration()); } + + @Test + public void shoudSetLogListener() { + LogListener listener = mock(LogListener.class); + Batch batch = Batch.builder().setLogListener(listener).build(); + assertThat(batch.getLoggingConfiguration().listener).isEqualTo(listener); + } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/LoggingConfigurationTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/LoggingConfigurationTest.java index 948fbc8177e..9b773b220e9 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/LoggingConfigurationTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/LoggingConfigurationTest.java @@ -19,58 +19,67 @@ */ package org.sonar.batch.bootstrapper; +import org.sonar.home.log.LogListener; + import com.google.common.collect.Maps; import org.junit.Test; import java.util.Map; +import static org.mockito.Mockito.mock; import static org.assertj.core.api.Assertions.assertThat; public class LoggingConfigurationTest { @Test public void testSetVerbose() { - assertThat(LoggingConfiguration.create(null).setVerbose(true) + assertThat(new LoggingConfiguration(null).setVerbose(true) .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_VERBOSE); - assertThat(LoggingConfiguration.create(null).setVerbose(false) + assertThat(new LoggingConfiguration(null).setVerbose(false) .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT); - assertThat(LoggingConfiguration.create(null).setRootLevel("ERROR") + assertThat(new LoggingConfiguration(null).setRootLevel("ERROR") .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("ERROR"); } @Test public void shouldNotBeVerboseByDefault() { - assertThat(LoggingConfiguration.create(null) + assertThat(new LoggingConfiguration(null) .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT); } + @Test + public void test_log_listener_setter() { + LogListener listener = mock(LogListener.class); + assertThat(new LoggingConfiguration(null).setListener(listener).listener).isEqualTo(listener); + } + @Test public void test_deprecated_log_properties() { Map properties = Maps.newHashMap(); - assertThat(LoggingConfiguration.create(null).setProperties(properties) + assertThat(new LoggingConfiguration(null).setProperties(properties) .getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT); properties.put("sonar.verbose", "true"); - LoggingConfiguration conf = LoggingConfiguration.create(null).setProperties(properties); + LoggingConfiguration conf = new LoggingConfiguration(null).setProperties(properties); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_VERBOSE); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); properties.put("sonar.verbose", "false"); - conf = LoggingConfiguration.create(null).setProperties(properties); + conf = new LoggingConfiguration(null).setProperties(properties); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo(LoggingConfiguration.LEVEL_ROOT_DEFAULT); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); properties.put("sonar.verbose", "false"); properties.put("sonar.log.profilingLevel", "FULL"); - conf = LoggingConfiguration.create(null).setProperties(properties); + conf = new LoggingConfiguration(null).setProperties(properties); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("DEBUG"); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("TRACE"); properties.put("sonar.verbose", "false"); properties.put("sonar.log.profilingLevel", "BASIC"); - conf = LoggingConfiguration.create(null).setProperties(properties); + conf = new LoggingConfiguration(null).setProperties(properties); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("DEBUG"); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); } @@ -78,53 +87,53 @@ public class LoggingConfigurationTest { @Test public void test_log_level_property() { Map properties = Maps.newHashMap(); - LoggingConfiguration conf = LoggingConfiguration.create(null).setProperties(properties); + LoggingConfiguration conf = new LoggingConfiguration(null).setProperties(properties); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("INFO"); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); properties.put("sonar.log.level", "INFO"); - conf = LoggingConfiguration.create(null).setProperties(properties); + conf = new LoggingConfiguration(null).setProperties(properties); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("INFO"); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); properties.put("sonar.log.level", "DEBUG"); - conf = LoggingConfiguration.create(null).setProperties(properties); + conf = new LoggingConfiguration(null).setProperties(properties); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("DEBUG"); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("WARN"); properties.put("sonar.log.level", "TRACE"); - conf = LoggingConfiguration.create(null).setProperties(properties); + conf = new LoggingConfiguration(null).setProperties(properties); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_ROOT_LOGGER_LEVEL)).isEqualTo("DEBUG"); assertThat(conf.getSubstitutionVariable(LoggingConfiguration.PROPERTY_SQL_LOGGER_LEVEL)).isEqualTo("TRACE"); } @Test public void testDefaultFormat() { - assertThat(LoggingConfiguration.create(null) + assertThat(new LoggingConfiguration(null) .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_DEFAULT); } @Test public void testMavenFormat() { - assertThat(LoggingConfiguration.create(new EnvironmentInformation("maven", "1.0")) + assertThat(new LoggingConfiguration(new EnvironmentInformation("maven", "1.0")) .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_MAVEN); } @Test public void testSetFormat() { - assertThat(LoggingConfiguration.create(null).setFormat("%d %level") + assertThat(new LoggingConfiguration(null).setFormat("%d %level") .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo("%d %level"); } @Test public void shouldNotSetBlankFormat() { - assertThat(LoggingConfiguration.create(null).setFormat(null) + assertThat(new LoggingConfiguration(null).setFormat(null) .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_DEFAULT); - assertThat(LoggingConfiguration.create(null).setFormat("") + assertThat(new LoggingConfiguration(null).setFormat("") .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_DEFAULT); - assertThat(LoggingConfiguration.create(null).setFormat(" ") + assertThat(new LoggingConfiguration(null).setFormat(" ") .getSubstitutionVariable(LoggingConfiguration.PROPERTY_FORMAT)).isEqualTo(LoggingConfiguration.FORMAT_DEFAULT); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/LoggingConfiguratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/LoggingConfiguratorTest.java new file mode 100644 index 00000000000..2f7311a39de --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/LoggingConfiguratorTest.java @@ -0,0 +1,139 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrapper; + +import org.sonar.home.log.LogListener; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; +import org.junit.Before; + +public class LoggingConfiguratorTest { + private static final String TEST_STR = "foo"; + private LoggingConfiguration conf = new LoggingConfiguration(); + private ByteArrayOutputStream out; + private SimpleLogListener listener; + + @Before + public void setUp() { + out = new ByteArrayOutputStream(); + conf = new LoggingConfiguration(); + listener = new SimpleLogListener(); + } + + private class SimpleLogListener implements LogListener { + String msg; + Level level; + + @Override + public void log(String msg, Level level) { + this.msg = msg; + this.level = level; + } + } + + @Test + public void testCustomAppender() throws UnsupportedEncodingException { + conf.setListener(listener); + LoggingConfigurator.apply(conf); + + Logger logger = LoggerFactory.getLogger(this.getClass()); + logger.info(TEST_STR); + + assertThat(listener.msg).endsWith(TEST_STR); + assertThat(listener.level).isEqualTo(LogListener.Level.INFO); + } + + @Test + public void testNoStdout() throws UnsupportedEncodingException { + System.setOut(new PrintStream(out, false, StandardCharsets.UTF_8.name())); + conf.setListener(listener); + LoggingConfigurator.apply(conf); + + Logger logger = LoggerFactory.getLogger(this.getClass()); + + logger.error(TEST_STR); + logger.info(TEST_STR); + logger.debug(TEST_STR); + assertThat(out.size()).isEqualTo(0); + } + + @Test + public void testFormatNoEffect() throws UnsupportedEncodingException { + conf.setListener(listener); + conf.setFormat("%t"); + + LoggingConfigurator.apply(conf); + Logger logger = LoggerFactory.getLogger(this.getClass()); + + logger.info("info"); + + assertThat(listener.msg).isEqualTo("info"); + } + + @Test + public void testSqlClasspath() throws UnsupportedEncodingException { + String classpath = "/org/sonar/batch/bootstrapper/logback.xml"; + + conf.setListener(listener); + conf.setShowSql(true); + + LoggingConfigurator.apply(conf, classpath); + + Logger logger = LoggerFactory.getLogger("java.sql"); + logger.info("foo"); + + assertThat(listener.msg).endsWith(TEST_STR); + } + + @Test + public void testNoListener() throws UnsupportedEncodingException { + System.setOut(new PrintStream(out, false, StandardCharsets.UTF_8.name())); + LoggingConfigurator.apply(conf); + + Logger logger = LoggerFactory.getLogger(this.getClass()); + logger.info("info"); + + assertThat(new String(out.toByteArray(), StandardCharsets.UTF_8)).contains("info"); + } + + @Test + public void testNoSqlClasspath() throws UnsupportedEncodingException { + String classpath = "/org/sonar/batch/bootstrapper/logback.xml"; + + conf.setListener(listener); + conf.setShowSql(false); + + LoggingConfigurator.apply(conf, classpath); + + Logger logger = LoggerFactory.getLogger("java.sql"); + logger.info("foo"); + + assertThat(listener.msg).isNull(); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/PrintStreamTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/PrintStreamTest.java new file mode 100644 index 00000000000..da833eae508 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrapper/PrintStreamTest.java @@ -0,0 +1,102 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.batch.bootstrapper; + +import ch.qos.logback.core.encoder.EchoEncoder; +import ch.qos.logback.classic.spi.ILoggingEvent; +import org.mockito.Matchers; +import ch.qos.logback.core.encoder.Encoder; +import ch.qos.logback.core.Context; +import org.junit.Test; +import org.junit.Before; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; + +import static org.assertj.core.api.Assertions.assertThat; + +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.mock; + +public class PrintStreamTest { + private static final String TEST_STR = "foo"; + + private ByteArrayOutputStream os; + private PrintStream stream; + private PrintStreamAppender appender; + private Context context = mock(Context.class); + + private Encoder encoder = mock(Encoder.class); + private ILoggingEvent event = mock(ILoggingEvent.class); + + @Before + public void setUp() { + os = new ByteArrayOutputStream(); + stream = new PrintStream(os); + + appender = new PrintStreamAppender(stream); + when(event.getMessage()).thenReturn(TEST_STR); + when(event.toString()).thenReturn(TEST_STR); + } + + @Test + public void testNullStream() { + appender.setContext(mock(Context.class)); + appender.setEncoder(encoder); + appender.setTarget(null); + appender.start(); + appender.doAppend(event); + + verifyNoMoreInteractions(encoder); + } + + @Test + public void testEncoder() throws IOException { + appender.setContext(mock(Context.class)); + appender.setEncoder(encoder); + appender.start(); + appender.doAppend(event); + + verify(encoder, times(1)).init(Matchers.notNull(OutputStream.class)); + verify(encoder, times(1)).doEncode(event); + + } + + @Test + public void testWrite() { + encoder = new EchoEncoder<>(); + encoder.setContext(context); + encoder.start(); + + appender.setContext(mock(Context.class)); + appender.setEncoder(encoder); + appender.setTarget(stream); + appender.start(); + + appender.doAppend(event); + + assertThat(os.toString()).isEqualTo(TEST_STR + System.lineSeparator()); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/ProjectTempFolder.java b/sonar-home/src/main/java/org/sonar/home/log/LogListener.java similarity index 56% rename from sonar-plugin-api/src/main/java/org/sonar/api/utils/ProjectTempFolder.java rename to sonar-home/src/main/java/org/sonar/home/log/LogListener.java index 276be831528..9ce4b8c668e 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/ProjectTempFolder.java +++ b/sonar-home/src/main/java/org/sonar/home/log/LogListener.java @@ -17,38 +17,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.api.utils; - -import javax.annotation.Nullable; - -import java.io.File; - -import org.sonar.api.batch.BatchSide; - - -/** - * Use this component to deal with temp files/folders that have a scope linked to each - * project analysis. - * Root location will typically be the working directory (see sonar.working.directory) - - * @since 5.2 - * - */ -@BatchSide -public interface ProjectTempFolder { - - /** - * Create a directory in temp folder with a random unique name. - */ - File newDir(); - - /** - * Create a directory in temp folder using provided name. - */ - File newDir(String name); - - File newFile(); - - File newFile(@Nullable String prefix, @Nullable String suffix); - -} \ No newline at end of file +package org.sonar.home.log; + +public interface LogListener { + public void log(String msg, Level level); + + public static enum Level { + ERROR, WARN, INFO, DEBUG, TRACE; + } +} -- 2.39.5