Browse Source

SONAR-5700 ability to change level of loggers of 3rd-party libraries

tags/5.1-RC1
Simon Brandhof 9 years ago
parent
commit
6adb553ddc

+ 0
- 79
microbenchmark-template/src/main/java/org/sonar/microbenchmark/LoggingBenchmark.java View File

@@ -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();
}
}

+ 1
- 2
server/sonar-process/src/main/java/org/sonar/process/LogbackHelper.java View 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();

+ 4
- 5
sonar-application/src/main/assembly/conf/sonar.properties View File

@@ -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

+ 5
- 0
sonar-plugin-api/pom.xml View File

@@ -155,6 +155,11 @@
<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>

+ 6
- 1
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/ConsoleLogger.java View 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));
}

+ 1
- 1
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/ConsoleLoggers.java View File

@@ -25,7 +25,7 @@ class ConsoleLoggers extends Loggers {

@Override
protected Logger newInstance(String name) {
return new ConsoleLogger(name);
return new ConsoleLogger();
}

@Override

+ 1
- 1
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogTester.java View 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>

+ 48
- 22
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogbackLogger.java View File

@@ -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;
}
}

+ 2
- 1
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LogbackLoggers.java View 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

+ 60
- 4
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/Logger.java View 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);
}

+ 24
- 0
sonar-plugin-api/src/main/java/org/sonar/api/utils/log/LoggerLevel.java View File

@@ -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
}

+ 5
- 0
sonar-plugin-api/src/test/java/org/sonar/api/utils/log/ConsoleLoggerTest.java View 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();
}
}

+ 17
- 1
sonar-plugin-api/src/test/java/org/sonar/api/utils/log/LogbackLoggerTest.java View 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();
}
}

+ 1
- 1
sonar-plugin-api/src/test/java/org/sonar/api/utils/log/NullInterceptorTest.java View 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);

Loading…
Cancel
Save