@@ -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(); | |||
} | |||
} |
@@ -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(); |
@@ -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 |
@@ -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> |
@@ -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)); | |||
} |
@@ -25,7 +25,7 @@ class ConsoleLoggers extends Loggers { | |||
@Override | |||
protected Logger newInstance(String name) { | |||
return new ConsoleLogger(name); | |||
return new ConsoleLogger(); | |||
} | |||
@Override |
@@ -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> |
@@ -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; | |||
} | |||
} |
@@ -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 |
@@ -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); | |||
} |
@@ -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 | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); |