From: Simon Brandhof Date: Tue, 17 Feb 2015 13:53:25 +0000 (+0100) Subject: SONAR-5700 ability to change level of loggers of 3rd-party libraries X-Git-Tag: 5.1-RC1~182 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=6adb553ddc2711cb88632be9bd96b4e91c51082e;p=sonarqube.git SONAR-5700 ability to change level of loggers of 3rd-party libraries --- diff --git a/microbenchmark-template/src/main/java/org/sonar/microbenchmark/LoggingBenchmark.java b/microbenchmark-template/src/main/java/org/sonar/microbenchmark/LoggingBenchmark.java deleted file mode 100644 index fa836bd72b6..00000000000 --- a/microbenchmark-template/src/main/java/org/sonar/microbenchmark/LoggingBenchmark.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.microbenchmark; - -import org.apache.commons.lang.StringUtils; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.RunnerException; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; -import org.slf4j.Logger; - -import java.util.Date; -import java.util.concurrent.TimeUnit; - -@OutputTimeUnit(TimeUnit.MILLISECONDS) -@State(Scope.Thread) -@Fork(1) -@Warmup(iterations = 3) -@Measurement(iterations = 3) -@BenchmarkMode(Mode.Throughput) -public class LoggingBenchmark { - - private Logger logger1 = org.slf4j.LoggerFactory.getLogger("microbenchmark1"); - private Logger logger2 = org.slf4j.LoggerFactory.getLogger("microbenchmark2"); - - @Benchmark - public void logback_converters() throws Exception { - logger1.warn("many arguments {} {} {} {} {} {}", "foo", 7, 4.5, 1234567890L, true, new Date()); - } - - @Benchmark - public void string_converters() throws Exception { - logger2.warn(String.format("many arguments %s %d %f %d %b %tc", "foo", 7, 4.5, 1234567890L, true, new Date())); - } - - @Benchmark - public void logback_no_args() throws Exception { - logger1.warn("no args"); - StringUtils.replaceOnce() - } - - - /** - * You can this benchmark with maven command-line (see run.sh) or by executing this method - * in IDE - */ - public static void main(String[] args) throws RunnerException { - Options opt = new OptionsBuilder() - .include(LoggingBenchmark.class.getSimpleName()) - .build(); - new Runner(opt).run(); - } -} diff --git a/server/sonar-process/src/main/java/org/sonar/process/LogbackHelper.java b/server/sonar-process/src/main/java/org/sonar/process/LogbackHelper.java index f45426f67b7..dc33e3c0b6c 100644 --- a/server/sonar-process/src/main/java/org/sonar/process/LogbackHelper.java +++ b/server/sonar-process/src/main/java/org/sonar/process/LogbackHelper.java @@ -204,8 +204,7 @@ public class LogbackHelper { FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy(); rollingPolicy.setContext(context); rollingPolicy.setFileNamePattern(StringUtils.replace(filePath, filenamePrefix + ".log", filenamePrefix + ".%i.log")); - rollingPolicy.setMaxIndex(1); - rollingPolicy.setMaxIndex(maxFiles); + rollingPolicy.setMinIndex(1); rollingPolicy.setMaxIndex(maxFiles); rollingPolicy.setParent(appender); rollingPolicy.start(); diff --git a/sonar-application/src/main/assembly/conf/sonar.properties b/sonar-application/src/main/assembly/conf/sonar.properties index ed1499555a9..e953a9d5546 100644 --- a/sonar-application/src/main/assembly/conf/sonar.properties +++ b/sonar-application/src/main/assembly/conf/sonar.properties @@ -245,10 +245,7 @@ #-------------------------------------------------------------------------------------------------- # LOGGING -# Level of information displayed in the logs: NONE (default), BASIC (functional information) -# and FULL (functional and technical details) -#sonar.log.profilingLevel=NONE - +# Enable debug logs in file sonar.log. #sonar.log.debug=false # Path to log files. Can be absolute or relative to installation directory. @@ -262,7 +259,9 @@ # - disabled if value is "none". That needs logs to be managed by an external system like logrotate. #sonar.log.rollingPolicy=time:yyyy-MM-dd -# Maximum number of files to keep if a rolling policy is enabled +# Maximum number of files to keep if a rolling policy is enabled. +# - maximum value is 20 on size rolling policy +# - unlimited on time rolling policy. Set to zero to disable old file purging. #sonar.log.maxFiles=7 # Access log is the list of all the HTTP requests received by server. If enabled, it is stored diff --git a/sonar-plugin-api/pom.xml b/sonar-plugin-api/pom.xml index fef2a0bed26..3bb3de8f2e1 100644 --- a/sonar-plugin-api/pom.xml +++ b/sonar-plugin-api/pom.xml @@ -155,6 +155,11 @@ logback-classic true + + ch.qos.logback + logback-core + true + junit junit diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/ConsoleLogger.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/ConsoleLogger.java index 93af4ef8785..1dee60c11d6 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/ConsoleLogger.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/ConsoleLogger.java @@ -35,7 +35,7 @@ class ConsoleLogger extends BaseLogger { private final PrintStream stream; - ConsoleLogger(String unusedName) { + ConsoleLogger() { this.stream = System.out; } @@ -142,6 +142,11 @@ class ConsoleLogger extends BaseLogger { thrown.printStackTrace(); } + @Override + public boolean setLevel(LoggerLevel level) { + return false; + } + private void log(String level, String msg) { this.stream.println(String.format("%s %s", level, msg)); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/ConsoleLoggers.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/ConsoleLoggers.java index a6a2f2524a0..726321e2956 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/ConsoleLoggers.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/ConsoleLoggers.java @@ -25,7 +25,7 @@ class ConsoleLoggers extends Loggers { @Override protected Logger newInstance(String name) { - return new ConsoleLogger(name); + return new ConsoleLogger(); } @Override diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogTester.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogTester.java index 9ee625e0c62..98ddc36cbf5 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogTester.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogTester.java @@ -29,7 +29,7 @@ import java.util.List; * This JUnit rule allows to configure and access logs in tests. By default * debug logs are enabled. *

- * Warning - not compatible with parallel execution of tests. + * Warning - not compatible with parallel execution of tests in the same JVM fork. *

* Example: *

diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogbackLogger.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogbackLogger.java
index 7313a2fc2a5..7b42e877ac5 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogbackLogger.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogbackLogger.java
@@ -19,106 +19,132 @@
  */
 package org.sonar.api.utils.log;
 
+import ch.qos.logback.classic.*;
+import ch.qos.logback.classic.Logger;
+
 import javax.annotation.Nullable;
 
 /**
- * Note that logback is accessed through SLF4J
+ * Logback is used in production.
  */
 class LogbackLogger extends BaseLogger {
 
-  private final transient org.slf4j.Logger slf4j;
+  private final transient ch.qos.logback.classic.Logger logback;
 
-  LogbackLogger(org.slf4j.Logger slf4j) {
-    this.slf4j = slf4j;
+  LogbackLogger(ch.qos.logback.classic.Logger logback) {
+    this.logback = logback;
   }
 
   @Override
   public boolean isDebugEnabled() {
-    return slf4j.isDebugEnabled();
+    return logback.isDebugEnabled();
   }
 
   @Override
   protected void doDebug(String msg) {
-    slf4j.debug(msg);
+    logback.debug(msg);
   }
 
   @Override
   protected void doDebug(String msg, @Nullable Object arg) {
-    slf4j.debug(msg, arg);
+    logback.debug(msg, arg);
   }
 
   @Override
   protected void doDebug(String msg, @Nullable Object arg1, @Nullable Object arg2) {
-    slf4j.debug(msg, arg1, arg2);
+    logback.debug(msg, arg1, arg2);
   }
 
   @Override
   protected void doDebug(String msg, Object... args) {
-    slf4j.debug(msg, args);
+    logback.debug(msg, args);
   }
 
   @Override
   protected void doInfo(String msg) {
-    slf4j.info(msg);
+    logback.info(msg);
   }
 
   @Override
   protected void doInfo(String msg, @Nullable Object arg) {
-    slf4j.info(msg, arg);
+    logback.info(msg, arg);
   }
 
   @Override
   protected void doInfo(String msg, @Nullable Object arg1, @Nullable Object arg2) {
-    slf4j.info(msg, arg1, arg2);
+    logback.info(msg, arg1, arg2);
   }
 
   @Override
   protected void doInfo(String msg, Object... args) {
-    slf4j.info(msg, args);
+    logback.info(msg, args);
   }
 
   @Override
   protected void doWarn(String msg) {
-    slf4j.warn(msg);
+    logback.warn(msg);
   }
 
   @Override
   protected void doWarn(String msg, @Nullable Object arg) {
-    slf4j.warn(msg, arg);
+    logback.warn(msg, arg);
   }
 
   @Override
   protected void doWarn(String msg, @Nullable Object arg1, @Nullable Object arg2) {
-    slf4j.warn(msg, arg1, arg2);
+    logback.warn(msg, arg1, arg2);
   }
 
   @Override
   protected void doWarn(String msg, Object... args) {
-    slf4j.warn(msg, args);
+    logback.warn(msg, args);
   }
 
   @Override
   protected void doError(String msg) {
-    slf4j.error(msg);
+    logback.error(msg);
   }
 
   @Override
   protected void doError(String msg, @Nullable Object arg) {
-    slf4j.error(msg, arg);
+    logback.error(msg, arg);
   }
 
   @Override
   protected void doError(String msg, @Nullable Object arg1, @Nullable Object arg2) {
-    slf4j.error(msg, arg1, arg2);
+    logback.error(msg, arg1, arg2);
   }
 
   @Override
   protected void doError(String msg, Object... args) {
-    slf4j.error(msg, args);
+    logback.error(msg, args);
   }
 
   @Override
   protected void doError(String msg, Throwable thrown) {
-    slf4j.error(msg, thrown);
+    logback.error(msg, thrown);
+  }
+
+  @Override
+  public boolean setLevel(LoggerLevel level) {
+    switch (level) {
+      case DEBUG:
+        logback.setLevel(Level.DEBUG);
+        break;
+      case INFO:
+        logback.setLevel(Level.INFO);
+        break;
+      case WARN:
+        logback.setLevel(Level.WARN);
+        break;
+      case ERROR:
+        logback.setLevel(Level.ERROR);
+        break;
+    }
+    return true;
+  }
+
+  Logger logbackLogger() {
+    return logback;
   }
 }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogbackLoggers.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogbackLoggers.java
index e4490a79e3a..7d8ce45f94e 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogbackLoggers.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogbackLoggers.java
@@ -30,7 +30,8 @@ class LogbackLoggers extends Loggers {
 
   @Override
   protected Logger newInstance(String name) {
-    return new LogbackLogger(LoggerFactory.getLogger(name));
+    // logback is accessed through SLF4J
+    return new LogbackLogger((ch.qos.logback.classic.Logger)LoggerFactory.getLogger(name));
   }
 
   @Override
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/Logger.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/Logger.java
index 834dc235661..d729a4b7f08 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/Logger.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/Logger.java
@@ -23,7 +23,7 @@ import javax.annotation.Nullable;
 
 /**
  * SonarQube plugins are not coupled with external logging libraries like SLF4J or Logback.
- *
+ * 

* Example: *

  * public class MyClass {
@@ -35,7 +35,14 @@ import javax.annotation.Nullable;
  *   }
  * }
  * 
+ *

+ * Message arguments are defined with {}, but not with {@link java.util.Formatter} syntax. * + *

+ * INFO, WARN and ERROR levels are always enabled. They can't be disabled by users. + * DEBUG level can be enabled with properties sonar.log.debug (on server, see sonar.properties) + * and sonar.verbose (on batch) + *

* See {@link org.sonar.api.utils.log.LogTester} for testing facilities. * @since 5.1 */ @@ -44,15 +51,24 @@ public interface Logger { boolean isDebugEnabled(); /** - * Logs a DEBUG level message. Debug messages must - * be valuable for production environments and are not for development debugging. + * Logs a DEBUG message. Debug messages must + * be valuable for diagnosing production problems. They must not be used for development debugging. */ void debug(String msg); + /** + * @see #debug(String) + */ void debug(String pattern, @Nullable Object arg); + /** + * @see #debug(String) + */ void debug(String msg, @Nullable Object arg1, @Nullable Object arg2); + /** + * @see #debug(String) + */ void debug(String msg, Object... args); /** @@ -60,10 +76,19 @@ public interface Logger { */ void info(String msg); + /** + * @see #info(String) + */ void info(String msg, @Nullable Object arg); + /** + * @see #info(String) + */ void info(String msg, @Nullable Object arg1, @Nullable Object arg2); + /** + * @see #info(String) + */ void info(String msg, Object... args); /** @@ -71,10 +96,19 @@ public interface Logger { */ void warn(String msg); + /** + * @see #warn(String) + */ void warn(String msg, @Nullable Object arg); + /** + * @see #warn(String) + */ void warn(String msg, @Nullable Object arg1, @Nullable Object arg2); + /** + * @see #warn(String) + */ void warn(String msg, Object... args); /** @@ -82,14 +116,36 @@ public interface Logger { */ void error(String msg); + /** + * @see #error(String) + */ void error(String msg, @Nullable Object arg); + /** + * @see #error(String) + */ void error(String msg, @Nullable Object arg1, @Nullable Object arg2); + /** + * @see #error(String) + */ void error(String msg, Object... args); /** - * Logs an ERROR level message. + * @see #error(String) */ void error(String msg, Throwable thrown); + + /** + * Attempt to change logger level. Return true if it succeeded, false if + * the underlying logging facility does not allow to change level at + * runtime. + *

+ * This method must not be used to enable debug logs in tests. Use + * {@link org.sonar.api.utils.log.LogTester#enableDebug(boolean)}. + *

+ * The standard use-case is to customize logging of embedded 3rd-party + * libraries. + */ + boolean setLevel(LoggerLevel level); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LoggerLevel.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LoggerLevel.java new file mode 100644 index 00000000000..5ff6fd27f54 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LoggerLevel.java @@ -0,0 +1,24 @@ +/* + * 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.api.utils.log; + +public enum LoggerLevel { + DEBUG, INFO, WARN, ERROR +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/log/ConsoleLoggerTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/log/ConsoleLoggerTest.java index 68d4d02bac3..3f59d8d87e5 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/utils/log/ConsoleLoggerTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/log/ConsoleLoggerTest.java @@ -79,4 +79,9 @@ public class ConsoleLoggerTest { sut.error("message", new IllegalArgumentException()); verify(stream, times(5)).println(startsWith("ERROR ")); } + + @Test + public void level_change_not_implemented_yet() throws Exception { + assertThat(sut.setLevel(LoggerLevel.DEBUG)).isFalse(); + } } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/log/LogbackLoggerTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/log/LogbackLoggerTest.java index 2c8c3680eb7..7bb3ff87068 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/utils/log/LogbackLoggerTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/log/LogbackLoggerTest.java @@ -19,6 +19,7 @@ */ package org.sonar.api.utils.log; +import ch.qos.logback.classic.Level; import org.junit.Rule; import org.junit.Test; import org.slf4j.LoggerFactory; @@ -27,7 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class LogbackLoggerTest { - LogbackLogger sut = new LogbackLogger(LoggerFactory.getLogger(getClass())); + LogbackLogger sut = new LogbackLogger((ch.qos.logback.classic.Logger)LoggerFactory.getLogger(getClass())); @Rule public LogTester tester = new LogTester(); @@ -64,6 +65,21 @@ public class LogbackLoggerTest { sut.error("message {} {}", "foo", "bar"); sut.error("message {} {} {}", "foo", "bar", "baz"); sut.error("message", new IllegalArgumentException("")); + } + + @Test + public void change_level() throws Exception { + assertThat(sut.setLevel(LoggerLevel.ERROR)).isTrue(); + assertThat(sut.logbackLogger().getLevel()).isEqualTo(Level.ERROR); + assertThat(sut.setLevel(LoggerLevel.WARN)).isTrue(); + assertThat(sut.logbackLogger().getLevel()).isEqualTo(Level.WARN); + + assertThat(sut.setLevel(LoggerLevel.INFO)).isTrue(); + assertThat(sut.logbackLogger().getLevel()).isEqualTo(Level.INFO); + + assertThat(sut.setLevel(LoggerLevel.DEBUG)).isTrue(); + assertThat(sut.logbackLogger().getLevel()).isEqualTo(Level.DEBUG); + assertThat(sut.isDebugEnabled()).isTrue(); } } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/log/NullInterceptorTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/log/NullInterceptorTest.java index d25d04d1dcd..ca4d14e4890 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/utils/log/NullInterceptorTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/log/NullInterceptorTest.java @@ -26,7 +26,7 @@ import static org.mockito.Mockito.mock; public class NullInterceptorTest { @Test - public void do_nothing() throws Exception { + public void do_not_throws_exception() throws Exception { // verify that... it does nothing NullInterceptor.NULL_INSTANCE.log("foo"); NullInterceptor.NULL_INSTANCE.log("foo {}", 42);