SBT 将项目 ID 添加到多项目构建的日志中
SBT add projectID to logs in multiproject build
在 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 专家。
查看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
这样的日志库,而不是用自己的日志框架重新发明轮子,这是可能的。 :-(
正如 Rich 所说,目前还没有自定义 sbt 日志格式的扩展点。但是如果你不介意依赖内部 APIs 你可以接近你想要的,这取决于你使用的是哪个版本的 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
中日志实现已移动的 类,因为它们都是私有的、密封的、最终的等。我唯一能想到的就是复制 [的功能=18=] 是破解输出流:
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 多项目构建中,当您 运行 聚合器项目上的任务并且它 运行 是每个聚合子项目中的任务时,每个子项目的所有日志都会一起输出在一个大流中。
这使得调试多项目构建中的构建问题变得困难,因为所有日志都混合在一起。有没有办法在每一行日志中输出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 专家。
查看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
这样的日志库,而不是用自己的日志框架重新发明轮子,这是可能的。 :-(
正如 Rich 所说,目前还没有自定义 sbt 日志格式的扩展点。但是如果你不介意依赖内部 APIs 你可以接近你想要的,这取决于你使用的是哪个版本的 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
中日志实现已移动的 类,因为它们都是私有的、密封的、最终的等。我唯一能想到的就是复制 [的功能=18=] 是破解输出流:
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配置文件来自定义格式