diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml
index f86a523719..f0f05bb74b 100644
--- a/pmd-core/pom.xml
+++ b/pmd-core/pom.xml
@@ -75,6 +75,10 @@
provided
+
+ org.slf4j
+ slf4j-api
+
org.antlr
antlr4-runtime
@@ -114,6 +118,11 @@
pcollections
+
+ org.slf4j
+ slf4j-simple
+ test
+
com.github.tomakehurst
wiremock
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java
index 43ec00cd91..2ab7664862 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java
@@ -21,9 +21,10 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.logging.ConsoleHandler;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.event.Level;
import net.sourceforge.pmd.Report.GlobalReportBuilderListener;
import net.sourceforge.pmd.benchmark.TextTimingReportRenderer;
@@ -35,6 +36,7 @@ import net.sourceforge.pmd.benchmark.TimingReportRenderer;
import net.sourceforge.pmd.cache.NoopAnalysisCache;
import net.sourceforge.pmd.cli.PMDCommandLineInterface;
import net.sourceforge.pmd.cli.PmdParametersParseResult;
+import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration;
import net.sourceforge.pmd.internal.util.AssertionUtil;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageFilenameFilter;
@@ -52,7 +54,6 @@ import net.sourceforge.pmd.util.database.DBURI;
import net.sourceforge.pmd.util.database.SourceObject;
import net.sourceforge.pmd.util.datasource.DataSource;
import net.sourceforge.pmd.util.datasource.ReaderDataSource;
-import net.sourceforge.pmd.util.log.ScopedLogHandlersManager;
/**
* This is the main class for interacting with PMD. The primary flow of all Rule
@@ -62,7 +63,8 @@ import net.sourceforge.pmd.util.log.ScopedLogHandlersManager;
*/
public final class PMD {
- private static final Logger LOG = Logger.getLogger(PMD.class.getName());
+ // not final, in order to re-initialize logging
+ private static Logger log = LoggerFactory.getLogger(PMD.class);
/**
* The line delimiter used by PMD in outputs. Usually the platform specific
@@ -97,19 +99,17 @@ public final class PMD {
try {
DBURI dbUri = new DBURI(uriString);
DBMSMetadata dbmsMetadata = new DBMSMetadata(dbUri);
- LOG.log(Level.FINE, "DBMSMetadata retrieved");
+ log.debug("DBMSMetadata retrieved");
List sourceObjectList = dbmsMetadata.getSourceObjectList();
- LOG.log(Level.FINE, "Located {0} database source objects", sourceObjectList.size());
+ log.debug("Located {} database source objects", sourceObjectList.size());
for (SourceObject sourceObject : sourceObjectList) {
String falseFilePath = sourceObject.getPseudoFileName();
- LOG.log(Level.FINEST, "Adding database source object {0}", falseFilePath);
+ log.trace("Adding database source object {}", falseFilePath);
try {
dataSources.add(new ReaderDataSource(dbmsMetadata.getSourceCode(sourceObject), falseFilePath));
} catch (SQLException ex) {
- if (LOG.isLoggable(Level.WARNING)) {
- LOG.log(Level.WARNING, "Cannot get SourceCode for " + falseFilePath + " - skipping ...", ex);
- }
+ log.warn("Cannot get SourceCode for {} - skipping ...", falseFilePath, ex);
}
}
} catch (URISyntaxException e) {
@@ -160,14 +160,9 @@ public final class PMD {
}
return violationCounter.getResult();
} catch (final Exception e) {
- String message = e.getMessage();
- if (message == null) {
- LOG.log(Level.SEVERE, "Exception during processing", e);
- } else {
- LOG.severe(message);
- }
- LOG.log(Level.FINE, "Exception during processing", e);
- LOG.info(PMDCommandLineInterface.buildUsageText());
+ log.error("Exception during processing: {}", e.toString()); // only exception without stacktrace
+ log.debug("Exception during processing", e); // with stacktrace
+ log.info(PMDCommandLineInterface.buildUsageText());
return PMDCommandLineInterface.NO_ERRORS_STATUS;
} finally {
/*
@@ -190,11 +185,11 @@ public final class PMD {
if (isEmpty(ruleSets)) {
String msg = "No rules found. Maybe you misspelled a rule name? ("
+ String.join(",", rulesetPaths) + ')';
- LOG.log(Level.SEVERE, msg);
+ log.error(msg);
throw new IllegalArgumentException(msg);
}
} catch (RuleSetLoadException rsnfe) {
- LOG.log(Level.SEVERE, "Ruleset not found", rsnfe);
+ log.error("Ruleset not found", rsnfe);
throw rsnfe;
}
return ruleSets;
@@ -211,10 +206,10 @@ public final class PMD {
* @param rulesets the RuleSets to print
*/
private static void printRuleNamesInDebug(List rulesets) {
- if (LOG.isLoggable(Level.FINER)) {
+ if (log.isDebugEnabled()) {
for (RuleSet rset : rulesets) {
for (Rule r : rset.getRules()) {
- LOG.finer("Loaded rule " + r.getName());
+ log.debug("Loaded rule {}", r.getName());
}
}
}
@@ -335,10 +330,7 @@ public final class PMD {
ruleSets.removeDysfunctionalRules(brokenRules);
for (final Rule rule : brokenRules) {
- if (LOG.isLoggable(Level.WARNING)) {
- LOG.log(Level.WARNING,
- "Removed misconfigured rule: " + rule.getName() + " cause: " + rule.dysfunctionReason());
- }
+ log.warn("Removed misconfigured rule: {} cause: {}", rule.getName(), rule.dysfunctionReason());
}
return brokenRules;
@@ -368,11 +360,11 @@ public final class PMD {
private static void encourageToUseIncrementalAnalysis(final PMDConfiguration configuration) {
if (!configuration.isIgnoreIncrementalAnalysis()
&& configuration.getAnalysisCache() instanceof NoopAnalysisCache
- && LOG.isLoggable(Level.WARNING)) {
+ && log.isWarnEnabled()) {
final String version =
PMDVersion.isUnknown() || PMDVersion.isSnapshot() ? "latest" : "pmd-" + PMDVersion.VERSION;
- LOG.warning("This analysis could be faster, please consider using Incremental Analysis: "
- + "https://pmd.github.io/" + version + "/pmd_userdocs_incremental_analysis.html");
+ log.warn("This analysis could be faster, please consider using Incremental Analysis: "
+ + "https://pmd.github.io/{}/pmd_userdocs_incremental_analysis.html", version);
}
}
@@ -433,7 +425,7 @@ public final class PMD {
String filePaths = FileUtil.readFilelist(file);
files.removeAll(FileUtil.collectFiles(filePaths, fileSelector));
} catch (IOException ex) {
- LOG.log(Level.SEVERE, "Problem with Ignore File", ex);
+ log.error("Problem with Ignore File", ex);
throw new RuntimeException("Problem with Ignore File Path: " + ignoreFilePath, ex);
}
}
@@ -451,9 +443,7 @@ public final class PMD {
final LanguageVersion version = discoverer.getDefaultLanguageVersion(ruleLanguage);
if (RuleSet.applies(rule, version)) {
languages.add(ruleLanguage);
- if (LOG.isLoggable(Level.FINE)) {
- LOG.fine("Using " + ruleLanguage.getShortName() + " version: " + version.getShortName());
- }
+ log.debug("Using {} version: {}", ruleLanguage.getShortName(), version.getShortName());
}
}
}
@@ -506,8 +496,8 @@ public final class PMD {
if (!parseResult.getDeprecatedOptionsUsed().isEmpty()) {
Entry first = parseResult.getDeprecatedOptionsUsed().entrySet().iterator().next();
- LOG.warning("Some deprecated options were used on the command-line, including " + first.getKey());
- LOG.warning("Consider replacing it with " + first.getValue());
+ log.warn("Some deprecated options were used on the command-line, including {}", first.getKey());
+ log.warn("Consider replacing it with {}", first.getValue());
}
if (parseResult.isVersion()) {
@@ -541,12 +531,15 @@ public final class PMD {
TimeTracker.startGlobalTracking();
}
- final Level logLevel = configuration.isDebug() ? Level.FINER : Level.INFO;
- final ScopedLogHandlersManager logHandlerManager = new ScopedLogHandlersManager(logLevel, new ConsoleHandler());
- final Level oldLogLevel = LOG.getLevel();
- // Need to do this, since the static logger has already been initialized
- // at this point
- LOG.setLevel(logLevel);
+ final Level logLevel = configuration.isDebug() ? Level.DEBUG : Level.INFO;
+ Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(logLevel);
+ // need to reload the logger with the new configuration
+ log = LoggerFactory.getLogger(PMD.class);
+ if (configuration.isDebug()) {
+ log.debug("Loglevel is at {}", logLevel);
+ } else {
+ log.info("Loglevel is at {}", logLevel);
+ }
StatusCode status;
try {
@@ -562,9 +555,6 @@ public final class PMD {
System.err.println(e.getMessage());
status = StatusCode.ERROR;
} finally {
- logHandlerManager.close();
- LOG.setLevel(oldLogLevel);
-
if (configuration.isBenchmark()) {
final TimingReport timingReport = TimeTracker.stopGlobalTracking();
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/Slf4jSimpleConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/Slf4jSimpleConfiguration.java
new file mode 100644
index 0000000000..9967a87851
--- /dev/null
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/Slf4jSimpleConfiguration.java
@@ -0,0 +1,37 @@
+/*
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.internal;
+
+import java.lang.reflect.Method;
+
+import org.slf4j.ILoggerFactory;
+import org.slf4j.LoggerFactory;
+import org.slf4j.LoggerFactoryFriend;
+import org.slf4j.event.Level;
+
+public final class Slf4jSimpleConfiguration {
+ private Slf4jSimpleConfiguration() { }
+
+ public static void reconfigureDefaultLogLevel(Level level) {
+ System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", level.toString());
+
+ // Call SimpleLogger.init() by reflection.
+ // Alternatively: move the CLI related classes into an own module, add
+ // slf4j-simple as a runtime dependency and create a PmdSlf4jSimpleFriend class in
+ // the package org.slf4j.simple to gain access to this package-private init method.
+ ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory();
+ Class extends ILoggerFactory> loggerFactoryClass = loggerFactory.getClass();
+ try {
+ Class> simpleLoggerClass = loggerFactoryClass.getClassLoader().loadClass("org.slf4j.simple.SimpleLogger");
+ Method initMethod = simpleLoggerClass.getDeclaredMethod("init");
+ initMethod.setAccessible(true);
+ initMethod.invoke(null);
+ } catch (ReflectiveOperationException ex) {
+ System.err.println("Error while initializing logging: " + ex);
+ }
+
+ LoggerFactoryFriend.reset();
+ }
+}
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java
index 6437b7a8b7..a9f5fa4d99 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/cli/CoreCliTest.java
@@ -21,10 +21,10 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.RestoreSystemProperties;
+import org.junit.contrib.java.lang.system.SystemErrRule;
import org.junit.rules.TemporaryFolder;
import net.sourceforge.pmd.PMD;
-import net.sourceforge.pmd.junit.JavaUtilLoggingRule;
/**
*
@@ -39,7 +39,7 @@ public class CoreCliTest {
@Rule
public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
@Rule
- public JavaUtilLoggingRule loggingRule = new JavaUtilLoggingRule(PMD.class.getPackage().getName()).mute();
+ public SystemErrRule loggingRule = new SystemErrRule().enableLog().muteForSuccessfulTests();
private Path srcDir;
@@ -163,6 +163,18 @@ public class CoreCliTest {
}
}
+ @Test
+ public void debugLogging() {
+ runPmdSuccessfully("--debug", "--no-cache", "--dir", srcDir, "--rulesets", DUMMY_RULESET);
+ loggingRule.getLog().contains("[main] DEBUG net.sourceforge.pmd.PMD - Loglevel is at DEBUG");
+ }
+
+ @Test
+ public void defaultLogging() {
+ runPmdSuccessfully("--no-cache", "--dir", srcDir, "--rulesets", DUMMY_RULESET);
+ loggingRule.getLog().contains("[main] INFO net.sourceforge.pmd.PMD - Loglevel is at INFO");
+ }
+
// utilities
diff --git a/pmd-test/pom.xml b/pmd-test/pom.xml
index 31ffb0a5e5..ba77537131 100644
--- a/pmd-test/pom.xml
+++ b/pmd-test/pom.xml
@@ -49,6 +49,10 @@
system-rules
compile
+
+ org.slf4j
+ slf4j-simple
+
org.mockito
mockito-core
diff --git a/pom.xml b/pom.xml
index 2476b7959d..2ca546d3c8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -98,6 +98,7 @@
1.10.12
3.2.0
4.8
+ 2.0.0-alpha6
UTF-8
UTF-8
@@ -717,7 +718,12 @@
org.slf4j
slf4j-api
- 1.7.32
+ ${slf4j.version}
+
+
+ org.slf4j
+ slf4j-simple
+ ${slf4j.version}
org.codehaus.groovy