From 683a559b379583933637feef2867cf8da91b74d3 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 30 Aug 2025 18:53:42 -0400 Subject: [PATCH] Remove log4j --- build.sbt | 11 +- .../sbt/internal/util/BufferedLogger.scala | 48 ++------ .../sbt/internal/util/ConsoleAppender.scala | 110 +----------------- .../src/main/scala/sbt/util/LogExchange.scala | 41 +------ main/src/main/scala/sbt/Defaults.scala | 17 +-- main/src/main/scala/sbt/Keys.scala | 4 +- main/src/main/scala/sbt/ScriptedPlugin.scala | 3 - .../update-sbt-classifiers/build.sbt | 3 - 8 files changed, 21 insertions(+), 216 deletions(-) diff --git a/build.sbt b/build.sbt index 408bb0796..daf9e6051 100644 --- a/build.sbt +++ b/build.sbt @@ -10,7 +10,7 @@ import com.eed3si9n.jarjarabrams.ModuleCoordinate // ThisBuild settings take lower precedence, // but can be shared across the multi projects. ThisBuild / version := { - val v = "2.0.0-SNAPSHOT" + val v = "2.0.0-RC4-bin-SNAPSHOT" nightlyVersion.getOrElse(v) } ThisBuild / Utils.version2_13 := "2.0.0-SNAPSHOT" @@ -146,6 +146,7 @@ def mimaSettingsSince(versions: Seq[String]): Seq[Def.Setting[?]] = Def settings exclude[FinalMethodProblem]("sbt.internal.*"), exclude[IncompatibleResultTypeProblem]("sbt.internal.*"), exclude[ReversedMissingMethodProblem]("sbt.internal.*"), + exclude[DirectMissingMethodProblem]("sbt.Keys.extraLoggers"), ), ) @@ -319,8 +320,6 @@ lazy val utilLogging = project jline3Terminal, jline3JNI, jline3Native, - log4jApi, - log4jCore, disruptor, sjsonNewScalaJson.value, ), @@ -338,6 +337,8 @@ lazy val utilLogging = project Test / fork := true, mimaSettings, mimaBinaryIssueFilters ++= Seq( + exclude[MissingClassProblem]("sbt.internal.util.ConsoleAppenderFromLog4J"), + exclude[MissingClassProblem]("sbt.internal.util.Log4JConsoleAppender"), ), ) .configure(addSbtIO) @@ -711,13 +712,13 @@ lazy val mainProj = (project in file("main")) } }, libraryDependencies ++= - (Seq( + Seq( scalaXml, sjsonNewScalaJson.value, sjsonNewCore.value, launcherInterface, caffeine, - ) ++ log4jModules), + ), libraryDependencies ++= (scalaVersion.value match { case v if v.startsWith("2.12.") => List() case _ => List(scalaPar) diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/BufferedLogger.scala b/internal/util-logging/src/main/scala/sbt/internal/util/BufferedLogger.scala index 105612d73..8ec136c62 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/BufferedLogger.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/BufferedLogger.scala @@ -10,11 +10,7 @@ package sbt.internal.util import sbt.util.* import scala.collection.mutable.ListBuffer -import org.apache.logging.log4j.core.{ LogEvent as XLogEvent } -import org.apache.logging.log4j.core.appender.AbstractAppender -import org.apache.logging.log4j.core.layout.PatternLayout import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.atomic.AtomicReference object BufferedAppender { def generateName: String = @@ -36,50 +32,24 @@ object BufferedAppender { * logged is used, not the level at the time 'play' is called. */ class BufferedAppender(override val name: String, delegate: Appender) extends Appender { - override def close(): Unit = log4j.get match { - case null => - case a => a.stop() - } + override def close(): Unit = () override private[sbt] def properties: ConsoleAppender.Properties = delegate.properties override private[sbt] def suppressedMessage: SuppressedTraceContext => Option[String] = delegate.suppressedMessage - private val log4j = new AtomicReference[AbstractAppender] - override private[sbt] def toLog4J = log4j.get match { - case null => - val a = new AbstractAppender( - delegate.name + "-log4j", - null, - PatternLayout.createDefaultLayout(), - true, - Array.empty - ) { - start() - override def append(event: XLogEvent): Unit = { - if (recording) { - Util.ignoreResult(buffer.add(Left(event.toImmutable))) - } else { - delegate.toLog4J.append(event) - } - } - } - log4j.set(a) - a - case a => a - } private val buffer = - new java.util.Vector[Either[XLogEvent, (Level.Value, Option[String], Option[ObjectEvent[?]])]] + new java.util.Vector[(Level.Value, Option[String], Option[ObjectEvent[?]])] private var recording = false override def appendLog(level: Level.Value, message: => String): Unit = { - if (recording) Util.ignoreResult(buffer.add(Right((level, Some(message), None)))) + if (recording) Util.ignoreResult(buffer.add((level, Some(message), None))) else delegate.appendLog(level, message) } override private[sbt] def appendObjectEvent[T]( level: Level.Value, message: => ObjectEvent[T] ): Unit = { - if (recording) Util.ignoreResult(buffer.add(Right(((level, None, Some(message)))))) + if (recording) Util.ignoreResult(buffer.add(((level, None, Some(message))))) else delegate.appendObjectEvent(level, message) } @@ -113,12 +83,10 @@ class BufferedAppender(override val name: String, delegate: Appender) extends Ap */ def play(): Unit = synchronized { - buffer.forEach { - case Right((l, Some(m), _)) => delegate.appendLog(l, m) - case Right((l, _, Some(oe))) => delegate.appendObjectEvent(l, oe) - case Left(x) => delegate.toLog4J.append(x) - case _ => - } + buffer.forEach: + case (l, Some(m), _) => delegate.appendLog(l, m) + case (l, _, Some(oe)) => delegate.appendObjectEvent(l, oe) + case _ => buffer.clear() } diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala index 369813959..e1403b484 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala @@ -10,18 +10,10 @@ package sbt.internal.util import java.io.{ PrintStream, PrintWriter } import java.lang.StringBuilder -import java.nio.channels.ClosedChannelException import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger } -import org.apache.logging.log4j.core.appender.AbstractAppender -import org.apache.logging.log4j.core.{ Appender as XAppender, LogEvent as XLogEvent } -import org.apache.logging.log4j.message.{ Message, ObjectMessage, ReusableObjectMessage } -import org.apache.logging.log4j.{ Level as XLevel } import sbt.internal.util.ConsoleAppender.* import sbt.util.* -import org.apache.logging.log4j.core.AbstractLogEvent -import org.apache.logging.log4j.message.SimpleMessageFactory -import java.util.concurrent.atomic.AtomicReference object ConsoleLogger { @@ -325,41 +317,6 @@ object ConsoleAppender { ) } - /** - * Converts the Log4J `level` to the corresponding sbt level. - * - * @param level - * A level, as represented by Log4J. - * @return - * The corresponding level in sbt's world. - */ - def toLevel(level: XLevel): Level.Value = - level match { - case XLevel.OFF => Level.Debug - case XLevel.FATAL => Level.Error - case XLevel.ERROR => Level.Error - case XLevel.WARN => Level.Warn - case XLevel.INFO => Level.Info - case XLevel.DEBUG => Level.Debug - case _ => Level.Debug - } - - /** - * Converts the sbt `level` to the corresponding Log4J level. - * - * @param level - * A level, as represented by sbt. - * @return - * The corresponding level in Log4J's world. - */ - def toXLevel(level: Level.Value): XLevel = - level match { - case Level.Error => XLevel.ERROR - case Level.Warn => XLevel.WARN - case Level.Info => XLevel.INFO - case Level.Debug => XLevel.DEBUG - } - private[sbt] def generateName(): String = "out-" + generateId.incrementAndGet } @@ -378,33 +335,7 @@ class ConsoleAppender( override private[sbt] val properties: Properties, override private[sbt] val suppressedMessage: SuppressedTraceContext => Option[String] ) extends Appender { - private val log4j = new AtomicReference[XAppender](null) - override private[sbt] lazy val toLog4J = log4j.get match { - case null => - log4j.synchronized { - log4j.get match { - case null => - val l = new Log4JConsoleAppender( - name, - properties, - suppressedMessage, - { event => - val level = ConsoleAppender.toLevel(event.getLevel) - val message = event.getMessage - try appendMessage(level, message) - catch { case _: ClosedChannelException => } - } - ) - log4j.set(l) - l - case l => l - } - } - } - override def close(): Unit = log4j.get match { - case null => - case a => a.stop() - } + override def close(): Unit = () } trait Appender extends AutoCloseable { private[sbt] def name: String @@ -431,8 +362,6 @@ trait Appender extends AutoCloseable { */ def getTrace: Int = synchronized { traceEnabledVar } - private[sbt] def toLog4J: XAppender - /** * Logs the stack trace of `t`, possibly shortening it. * @@ -557,13 +486,6 @@ trait Appender extends AutoCloseable { out.println(toWrite) } - private[util] def appendMessage(level: Level.Value, msg: Message): Unit = - msg match { - case o: ObjectMessage => appendMessageContent(level, o.getParameter) - case o: ReusableObjectMessage => appendMessageContent(level, o.getParameter) - case _ => appendLog(level, msg.getFormattedMessage) - } - private def appendTraceEvent(te: TraceEvent): Unit = { val traceLevel = getTrace if (traceLevel >= 0) { @@ -616,35 +538,5 @@ trait Appender extends AutoCloseable { appendMessageContent(level, message) } -private[internal] class Log4JConsoleAppender( - override private[sbt] val name: String, - override private[sbt] val properties: Properties, - override private[sbt] val suppressedMessage: SuppressedTraceContext => Option[String], - appendEvent: XLogEvent => Unit, -) extends AbstractAppender(name, null, LogExchange.dummyLayout, true, Array.empty) - with Appender { - start() - override def close(): Unit = stop() - override private[sbt] def toLog4J: XAppender = this - override def append(event: XLogEvent): Unit = appendEvent(event) -} -private[sbt] class ConsoleAppenderFromLog4J( - override private[sbt] val name: String, - override private[sbt] val properties: Properties, - override private[sbt] val suppressedMessage: SuppressedTraceContext => Option[String], - val delegate: XAppender, -) extends Appender { - def this(name: String, delegate: XAppender) = - this(name, Properties.from(Terminal.get), _ => None, delegate) - override def close(): Unit = delegate.stop() - private[sbt] def toLog4J: XAppender = delegate - override def appendLog(level: sbt.util.Level.Value, message: => String): Unit = { - delegate.append(new AbstractLogEvent { - override def getLevel(): XLevel = ConsoleAppender.toXLevel(level) - override def getMessage(): Message = - SimpleMessageFactory.INSTANCE.newMessage(message.toString, Array.empty[AnyRef]) - }) - } -} final class SuppressedTraceContext(val traceLevel: Int, val useFormat: Boolean) diff --git a/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala b/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala index 95e5171bd..bd1e0cbac 100644 --- a/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala +++ b/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala @@ -8,27 +8,13 @@ package sbt.util -import org.apache.logging.log4j.core.config.LoggerConfig -import org.apache.logging.log4j.core.layout.PatternLayout -import org.apache.logging.log4j.core.{ LoggerContext as XLoggerContext } -import org.apache.logging.log4j.{ LogManager as XLogManager } -import sbt.internal.util.{ Appender, ManagedLogger, TraceEvent, SuccessEvent, Util } +import sbt.internal.util.{ Appender, ManagedLogger, TraceEvent, SuccessEvent } import sbt.internal.util.appmacro.StringTypeTag - -import java.util.concurrent.ConcurrentHashMap import scala.collection.concurrent -// http://logging.apache.org/log4j/2.x/manual/customconfig.html -// https://logging.apache.org/log4j/2.x/log4j-core/apidocs/index.html - sealed abstract class LogExchange { - private[sbt] lazy val context: XLoggerContext = init() private[sbt] val stringCodecs: concurrent.Map[String, ShowLines[?]] = concurrent.TrieMap() private[sbt] val builtInStringCodecs: Unit = initStringCodecs() - private[util] val configs = new ConcurrentHashMap[String, LoggerConfig] - private[util] def addConfig(name: String, config: LoggerConfig): Unit = - Util.ignoreResult(configs.putIfAbsent(name, config)) - private[util] def removeConfig(name: String): Option[LoggerConfig] = Option(configs.remove(name)) def logger(name: String): ManagedLogger = logger(name, None, None) def logger(name: String, channelName: Option[String], execId: Option[String]): ManagedLogger = @@ -55,21 +41,6 @@ sealed abstract class LogExchange { registerStringCodec[SuccessEvent] } - // This is a dummy layout to avoid casting error during PatternLayout.createDefaultLayout() - // that was originally used for ConsoleAppender. - // The stacktrace shows it's having issue initializing default DefaultConfiguration. - // Since we currently do not use Layout inside ConsoleAppender, the actual pattern is not relevant. - private[sbt] lazy val dummyLayout: PatternLayout = { - val _ = context - val ctx = XLogManager.getContext(false) match { case x: XLoggerContext => x } - val config = ctx.getConfiguration - val lo = PatternLayout.newBuilder - .withConfiguration(config) - .withPattern(PatternLayout.SIMPLE_CONVERSION_PATTERN) - .build - lo - } - def stringCodec[A](tag: String): Option[ShowLines[A]] = stringCodecs.get(tag) map { _.asInstanceOf[ShowLines[A]] } def hasStringCodec(tag: String): Boolean = @@ -82,15 +53,5 @@ sealed abstract class LogExchange { val tag = implicitly[StringTypeTag[A]] val _ = getOrElseUpdateStringCodec(tag.key, ev) } - - private[sbt] def init(): XLoggerContext = { - import org.apache.logging.log4j.core.config.Configurator - import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory - val builder = ConfigurationBuilderFactory.newConfigurationBuilder - builder.setConfigurationName("sbt.util.logging") - val ctx = Configurator.initialize(builder.build()) - ctx match { case x: XLoggerContext => x } - } - private[sbt] def init(name: String): XLoggerContext = new XLoggerContext(name) } object LogExchange extends LogExchange diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index fa802ff44..700132d1e 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -16,7 +16,6 @@ import lmcoursier.CoursierDependencyResolution import lmcoursier.definitions.{ Configuration as CConfiguration } import org.apache.ivy.core.module.descriptor.ModuleDescriptor import org.apache.ivy.core.module.id.ModuleRevisionId -import org.apache.logging.log4j.core.{ Appender as XAppender } import org.scalasbt.ipcsocket.Win32SecurityLevel import sbt.Def.{ Initialize, ScopedKey, Setting, SettingsDefinition, parsed } import sbt.Keys.* @@ -332,19 +331,11 @@ object Defaults extends BuildCommon { try onUnload.value(s) finally IO.delete(taskTemporaryDirectory.value) }, - // // extraLoggers is deprecated - SettingKey[ScopedKey[?] => Seq[XAppender]]("extraLoggers") :== { _ => - Nil + extraAppenders :== { + new AppenderSupplier: + def apply(s: ScopedKey[?]): Seq[Appender] = Nil }, - extraAppenders := { - val f = SettingKey[ScopedKey[?] => Seq[XAppender]]("extraLoggers").value - s => - f(s).map { - case a: Appender => a - case a => new ConsoleAppenderFromLog4J(a.getName, a) - } - }, - useLog4J :== SysProp.useLog4J, + useLog4J :== false, watchSources :== Nil, // Although this is deprecated, it can't be removed or it breaks += for legacy builds. skip :== false, taskTemporaryDirectory := { diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 63d0f83aa..4684f50e1 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -15,7 +15,6 @@ import lmcoursier.definitions.{ CacheLogger, ModuleMatchers, Reconciliation } import lmcoursier.{ CoursierConfiguration, FallbackDependency } import org.apache.ivy.core.module.descriptor.ModuleDescriptor import org.apache.ivy.core.module.id.ModuleRevisionId -import org.apache.logging.log4j.core.{ Appender as XAppender } import sbt.Def.* import sbt.KeyRanks.* import sbt.internal.InMemoryCacheStore.CacheStoreFactoryFactory @@ -62,8 +61,7 @@ object Keys { val showSuccess = settingKey[Boolean]("If true, displays a success message after running a command successfully.").withRank(CSetting) val showTiming = settingKey[Boolean]("If true, the command success message includes the completion time.").withRank(CSetting) val timingFormat = settingKey[java.text.DateFormat]("The format used for displaying the completion time.").withRank(CSetting) - @deprecated("", "1.4.0") - val extraLoggers = settingKey[ScopedKey[?] => Seq[XAppender]]("A function that provides additional loggers for a given setting.").withRank(DSetting) + @transient val extraAppenders = settingKey[AppenderSupplier]("A function that provides additional loggers for a given setting.").withRank(DSetting) val useLog4J = settingKey[Boolean]("Toggles whether or not to use log4j for sbt internal loggers.").withRank(Invisible) val logManager = settingKey[LogManager]("The log manager, which creates Loggers for different contexts.").withRank(DSetting) diff --git a/main/src/main/scala/sbt/ScriptedPlugin.scala b/main/src/main/scala/sbt/ScriptedPlugin.scala index 546da5c90..1f12dfefd 100644 --- a/main/src/main/scala/sbt/ScriptedPlugin.scala +++ b/main/src/main/scala/sbt/ScriptedPlugin.scala @@ -28,9 +28,6 @@ import sbt.nio.file.{ Glob, RecursiveGlob } object ScriptedPlugin extends AutoPlugin { - // Force Log4J to not use a thread context classloader otherwise it throws a CCE - sys.props(org.apache.logging.log4j.util.LoaderUtil.IGNORE_TCCL_PROPERTY) = "true" - object autoImport { val ScriptedConf = Configurations.config("scripted-sbt").hide val ScriptedLaunchConf = Configurations.config("scripted-sbt-launch").hide diff --git a/sbt-app/src/sbt-test/dependency-management/update-sbt-classifiers/build.sbt b/sbt-app/src/sbt-test/dependency-management/update-sbt-classifiers/build.sbt index aa05d343f..7c483b453 100644 --- a/sbt-app/src/sbt-test/dependency-management/update-sbt-classifiers/build.sbt +++ b/sbt-app/src/sbt-test/dependency-management/update-sbt-classifiers/build.sbt @@ -42,9 +42,6 @@ lazy val root = (project in file(".")) "net.java.dev.jna:jna", "net.java.dev.jna:jna-platform", "net.openhft:zero-allocation-hashing", - "org.apache.logging.log4j:log4j-api", - "org.apache.logging.log4j:log4j-core", - "org.apache.logging.log4j:log4j-slf4j-impl", "org.checkerframework:checker-qual", "org.fusesource.jansi:jansi", "org.jline:jline-builtins",