]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5700 ability to change level of loggers of 3rd-party libraries
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 17 Feb 2015 13:53:25 +0000 (14:53 +0100)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Thu, 19 Feb 2015 11:02:11 +0000 (12:02 +0100)
14 files changed:
microbenchmark-template/src/main/java/org/sonar/microbenchmark/LoggingBenchmark.java [deleted file]
server/sonar-process/src/main/java/org/sonar/process/LogbackHelper.java
sonar-application/src/main/assembly/conf/sonar.properties
sonar-plugin-api/pom.xml
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/ConsoleLogger.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/ConsoleLoggers.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogTester.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogbackLogger.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogbackLoggers.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/Logger.java
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LoggerLevel.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/utils/log/ConsoleLoggerTest.java
sonar-plugin-api/src/test/java/org/sonar/api/utils/log/LogbackLoggerTest.java
sonar-plugin-api/src/test/java/org/sonar/api/utils/log/NullInterceptorTest.java

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 (file)
index fa836bd..0000000
+++ /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();
-  }
-}
index f45426f67b799f28b1f2a28afb744d9539db0153..dc33e3c0b6c69d74a2c2e10960fbef06c17b778f 100644 (file)
@@ -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();
index ed1499555a988fcc4c0e8c775d20a074afeeaf11..e953a9d5546f48ab85c6051a693fa215866b919a 100644 (file)
 #--------------------------------------------------------------------------------------------------
 # 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.
 #    - 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
index fef2a0bed26af32db72156dba84006e677c4df92..3bb3de8f2e1f00ac8579eda5cb116f0b0dd92821 100644 (file)
       <artifactId>logback-classic</artifactId>
       <optional>true</optional>
     </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-core</artifactId>
+      <optional>true</optional>
+    </dependency>
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
index 93af4ef8785a49a7b0cee21697d628064a1fdc5c..1dee60c11d6fc088da1eddd5fd8a77bdfbafbb8e 100644 (file)
@@ -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));
   }
index a6a2f2524a09e64dbbf03d84035adacecb80994a..726321e2956239ac404fe2f7eab47f165c18aa1f 100644 (file)
@@ -25,7 +25,7 @@ class ConsoleLoggers extends Loggers {
 
   @Override
   protected Logger newInstance(String name) {
-    return new ConsoleLogger(name);
+    return new ConsoleLogger();
   }
 
   @Override
index 9ee625e0c62d6d0c09220b28048c89f331da3b9e..98ddc36cbf5f1dfd9c0d4cb598ddb01f00c9da35 100644 (file)
@@ -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.
  * <p/>
- * Warning - not compatible with parallel execution of tests.
+ * Warning - not compatible with parallel execution of tests in the same JVM fork.
  * <p/>
  * Example:
  * <pre>
index 7313a2fc2a5192b34453271a4e6864c25902a7b1..7b42e877ac58af6b16e03d522d5d2e324d05c6e5 100644 (file)
  */
 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;
   }
 }
index e4490a79e3a4792950389585438b3e1261becd63..7d8ce45f94e5f8bfc0f8fe8df762ba4f8d93d1b3 100644 (file)
@@ -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
index 834dc2356614c1f9db98a1c4244f6977d19789ae..d729a4b7f08ed0ee3bf8181062ca058ca4ab9167 100644 (file)
@@ -23,7 +23,7 @@ import javax.annotation.Nullable;
 
 /**
  * SonarQube plugins are not coupled with external logging libraries like SLF4J or Logback.
- *
+ * <p/>
  * Example:
  * <pre>
  * public class MyClass {
@@ -35,7 +35,14 @@ import javax.annotation.Nullable;
  *   }
  * }
  * </pre>
+ * <p/>
+ * Message arguments are defined with <code>{}</code>, but not with {@link java.util.Formatter} syntax.
  *
+ * <p/>
+ * INFO, WARN and ERROR levels are always enabled. They can't be disabled by users.
+ * DEBUG level can be enabled with properties <code>sonar.log.debug</code> (on server, see sonar.properties)
+ * and <code>sonar.verbose</code> (on batch)
+ * <p/>
  * 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.
+   * <p/>
+   * This method must not be used to enable debug logs in tests. Use
+   * {@link org.sonar.api.utils.log.LogTester#enableDebug(boolean)}.
+   * <p/>
+   * 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 (file)
index 0000000..5ff6fd2
--- /dev/null
@@ -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
+}
index 68d4d02bac37c564feac6517a8f0305999ff5093..3f59d8d87e545da41132d0d3ed503df190eb3299 100644 (file)
@@ -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();
+  }
 }
index 2c8c3680eb7fe8a54cf1f2b987b61cdf843b4101..7bb3ff87068aaafe46fbf1256a2a75474fd44599 100644 (file)
@@ -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();
   }
 }
index d25d04d1dcd88a3ef49ac2cd13b969859df8b9be..ca4d14e489023581f98f072fa887027c692d8460 100644 (file)
@@ -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);