SBT将项目ID添加到多项目构建中的日志中
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SBT将项目ID添加到多项目构建中的日志中相关的知识,希望对你有一定的参考价值。
在SBT多项目构建中,当您在聚合器项目上运行任务并且它在每个聚合子项目中运行任务时,来自每个子项目的所有日志将在一个大流中一起输出。
这使得在多项目构建中调试构建问题变得困难,因为所有日志混合在一起。有没有办法在每个日志行上输出projectID,以便您可以快速识别日志来自哪个子项目?
这是一个示例项目:
name := "my-multiproject-build"
lazy val ProjectOne = project
lazy val ProjectTwo = project
lazy val root = project.in( file(".") ).aggregate(ProjectOne, ProjectTwo)
(默认情况下会发生什么)
sbt package
[info] Packaging ProjectOne.jar ...
[info] Done packaging.
[info] Packaging ProjectTwo.jar ...
[info] Done packaging.
(我想要的是)
sbt package
[info] [ProjectOne] Packaging ProjectOne.jar ...
[info] [ProjectOne] Done packaging.
[info] [ProjectTwo] Packaging ProjectTwo.jar ...
[info] [ProjectTwo] Done packaging.
我试着调查SBT custom loggers,但遗憾的是文档有点稀疏,我绝不是SBT专家。
像Rich说的那样,目前没有可以自定义sbt日志格式的扩展点。但是,如果您不介意依赖内部API,您可以接近您想要的内容,具体取决于您使用的sbt版本。
基本上你必须替换默认的logManager
而不是添加extraLoggers
(虽然API类似)。
sbt 0.13.x
我们这里的工作看起来更简单。我们可以重用BufferedLogger
来避免将所有内容委托给ConsoleLogger
所涉及的样板:
logManager := LogManager.withScreenLogger { (_, state) =>
val console = ConsoleLogger(state.globalLogging.console)
new BufferedLogger(console) {
val project = projectID.value.name
override def log(level: Level.Value, message: => String): Unit =
console.log(level, s"[$project] $message")
}
}
sbt 1.0.x
此处更改了日志记录API以提供event logging。我们现在必须提供更灵活的log4j Appender
,但这使我们的工作更加困难。我们不能重用日志实现已经移动的sbt.internal
中的类,因为它们都是私有的,密封的,最终的等等。我唯一想到的是没有重复ConsoleAppender
的功能就是破解输出流:
logManager := LogManager.defaultManager(
ConsoleOut.printStreamOut(new PrintStream(System.out) {
val project = projectID.value.name
override def println(str: String): Unit = {
val (lvl, msg) = str.span(_ != ']')
super.println(s"$lvl] [$project$msg")
}
}))
请注意,无法保证将调用println
而不是其他一些print
方法。
我不知道是否可以使用log4j配置文件来自定义格式。
通过SBT代码,我认为这不太可能。
这是一个build.sbt
,可以完成你想要的大部分工作。
import sbt.Level
name := "my-multiproject-build"
lazy val ProjectOne = project
lazy val ProjectTwo = project
lazy val root = project.in( file(".") ).aggregate(ProjectOne, ProjectTwo)
val wrapLogger = (project: Project, inner: AbstractLogger) => {
new AbstractLogger {
override def log(level: Level.Value, message: => String): Unit = {
inner.log(
level,
"[" + project.id + "] " + message
)
}
override def setTrace(flag: Int): Unit = inner.setTrace(flag)
override def setLevel(newLevel: Level.Value): Unit = {
// MultiLogger keeps setting this to debug
inner.setLevel(Level.Info)
}
override def setSuccessEnabled(flag: Boolean): Unit = inner.setSuccessEnabled(flag)
override def logAll(events: Seq[LogEvent]): Unit = {
events.foreach(log)
}
override def control(event: _root_.sbt.ControlEvent.Value, message: => String): Unit
= inner.control(event, message)
override def successEnabled: Boolean = inner.successEnabled
override def getLevel = inner.getLevel
override def getTrace: Int = inner.getTrace
override def trace(t: => Throwable): Unit = inner.trace(t)
override def success(message: => String): Unit = inner.success(message)
}
}
extraLoggers in ProjectOne := {
val currentFunction = extraLoggers.value
(key: ScopedKey[_]) => {
val logger = wrapLogger(ProjectOne, ConsoleLogger())
logger.setLevel(Level.Info)
logger +: currentFunction(key)
}
}
extraLoggers in ProjectTwo := {
val currentFunction = extraLoggers.value
(key: ScopedKey[_]) => {
val logger = wrapLogger(ProjectTwo, ConsoleLogger())
logger.setLevel(Level.Info)
logger +: currentFunction(key)
}
}
现在,对于项目特定的日志,输出会重复:一次是项目名称前置,一次是没有项目名称。输出如下:
[info] Done packaging.
[info] [ProjectTwo] Done packaging.
[info] Done updating.
[info] [ProjectOne] Done updating.
ConsoleLogger
是在MainLogging.defaultScreen
构建的,没有扩展点可以让你操纵我能看到的日志消息。
如果SBT
使用了像logback
或log4j2
这样的记录库,而不是用自己的日志框架重新发明轮子,那么这是可能的。 :-(
以上是关于SBT将项目ID添加到多项目构建中的日志中的主要内容,如果未能解决你的问题,请参考以下文章
如何通过将 sbt-plugin 用作多项目构建中的依赖项来访问它的子项目?