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