From 6adb553ddc2711cb88632be9bd96b4e91c51082e Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Tue, 17 Feb 2015 14:53:25 +0100 Subject: SONAR-5700 ability to change level of loggers of 3rd-party libraries --- sonar-plugin-api/pom.xml | 5 ++ .../org/sonar/api/utils/log/ConsoleLogger.java | 7 ++- .../org/sonar/api/utils/log/ConsoleLoggers.java | 2 +- .../java/org/sonar/api/utils/log/LogTester.java | 2 +- .../org/sonar/api/utils/log/LogbackLogger.java | 70 +++++++++++++++------- .../org/sonar/api/utils/log/LogbackLoggers.java | 3 +- .../main/java/org/sonar/api/utils/log/Logger.java | 64 ++++++++++++++++++-- .../java/org/sonar/api/utils/log/LoggerLevel.java | 24 ++++++++ .../org/sonar/api/utils/log/ConsoleLoggerTest.java | 5 ++ .../org/sonar/api/utils/log/LogbackLoggerTest.java | 18 +++++- .../sonar/api/utils/log/NullInterceptorTest.java | 2 +- 11 files changed, 170 insertions(+), 32 deletions(-) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LoggerLevel.java (limited to 'sonar-plugin-api') 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); -- cgit v1.2.3