我想将一个构建任务的standardOutput
更改为file,因为稍后将由另一个任务对其进行解析。
但是,我想在终端中同时输出以查看构建中发生了什么。
这就是我将任务的输出更改为文件的方式:
task sampleTaskWithOutputToFile(type: Exec) {
commandLine 'someCommand', 'param1'
doFirst {
standardOutput = new FileOutputStream('someFolder/someFile.out')
}
}
据我了解,我可以编写自己的OutputStream
实现,同时输出到文件和标准System.out
,但我想使用现有的解决方案。
此外,我不能使用像tee
这样的unix工具,因为任务可以从任何操作系统启动(Mac OS,某些Linux甚至Windows ......)
谢谢!
答案 0 :(得分:17)
阐述Peter N关于TeeOutputStream的评论:
task sampleTaskWithOutputToFile(type: Exec) {
commandLine 'someCommand', 'param1'
doFirst {
standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
new FileOutputStream("someFolder/someFile.out"), System.out);
}
}
答案 1 :(得分:1)
这是一种适用于任何类型的Gradle Task
的解决方案,不仅适用于Exec
。
注意:我提供的所有示例都是采用Gradle惰性配置语法的build.gradle.kts格式。
在您的特定情况下,您要使用内存缓存:
tasks.register<Exec>("sampleTaskWithOutputToFile") {
commandLine ("someCommand", "param1")
val taskOutput = StringBuilder()
logging.addStandardOutputListener { taskOutput.append(it) }
doLast {
project.file("foo.output.txt").writeText(taskOutput.toString())
}
}
或直接写入文件:
tasks.register<Exec>("sampleTaskWithOutputToFile") {
commandLine ("someCommand", "param1")
lateinit var taskOutput: java.io.Writer
doFirst {
taskOutput = project.file("someFolder/someFile.out").writer()
}
logging.addStandardOutputListener { taskOutput.append(it) }
doLast {
// WARNING: if the task fails, this won't be executed and the file remains open.
// The memory cache version doesn't have this problem.
taskOutput.close()
}
}
作为更一般的答案,我们可以考虑将外部插件注册为foo
任务。
tasks.register("foo") {
doLast {
println("warning: some message")
}
}
在项目构建脚本中,可以捕获输出并对其执行操作,甚至可以实施“警告失败”模式。
tasks.named("foo") {
val taskOutput = StringBuilder()
logging.addStandardOutputListener { taskOutput.append(it) }
doLast {
// Usage of taskOutput must be in doLast, after all other task actions have been done.
// Be careful: if there's another `doLast {}` added after this, the output from that won't be considered.
if ("warning:" in taskOutput) {
throw Exception(
"""
There was a problem executing foo, please fix.
""".trimIndent()
)
}
}
}
答案 2 :(得分:0)
如果您想从插件或groovy / kotlin / java脚本中获取完整日志,A可能对您有所帮助。
我正在使用该类来通过我自己的插件将gradle记录到文件中。
您可以使用此类(可以将它从kotlin转换为另一种语言,因为它并不难):
(例如:gradle.useLogger(FileLogger(File(project.rootDir,“ log.log”)))))
import org.gradle.BuildListener
import org.gradle.BuildResult
import org.gradle.api.Project
import org.gradle.api.ProjectEvaluationListener
import org.gradle.api.ProjectState
import org.gradle.api.Task
import org.gradle.api.execution.TaskActionListener
import org.gradle.api.execution.TaskExecutionGraph
import org.gradle.api.execution.TaskExecutionGraphListener
import org.gradle.api.execution.TaskExecutionListener
import org.gradle.api.initialization.Settings
import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.StandardOutputListener
import org.gradle.api.tasks.TaskState
import org.gradle.internal.logging.events.OutputEvent
import org.gradle.internal.logging.events.OutputEventListener
import sk.kimbinogreen.gradle.extensions.deleteIfExists
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.PrintStream
@Suppress("UnstableApiUsage")
class FileLogger(val file: File) :
OutputEventListener,
StandardOutputListener,
BuildListener,
ProjectEvaluationListener,
TaskExecutionGraphListener,
TaskExecutionListener,
TaskActionListener {
init {
file.parentFile.mkdirs()
file.deleteIfExists()
file.createNewFile()
appendText("Build Started.")
}
override fun buildStarted(gradle: Gradle?) {
gradle?.useLogger(this)
}
override fun beforeSettings(settings: Settings?) {
}
override fun settingsEvaluated(settings: Settings?) {
}
override fun projectsLoaded(gradle: Gradle?) {
gradle?.useLogger(this)
}
override fun projectsEvaluated(gradle: Gradle?) {
gradle?.useLogger(this)
}
override fun buildFinished(result: BuildResult?) {
appendText("Build finished.")
appendError(result?.failure)
}
override fun onOutput(p0: OutputEvent?) {
p0?.let { appendText(it.toString()) }
}
override fun onOutput(p0: CharSequence?) {
p0?.let {
appendText(it.toString())
}
}
override fun beforeExecute(task: Task) {
appendText("Task [${task.project.name}:${task.name}] started.")
task.logging.addStandardErrorListener(this)
task.logging.addStandardOutputListener(this)
}
override fun afterExecute(task: Task, state: TaskState) {
appendText("Task [${task.project.name}:${task.name}] finished.")
appendError(state.failure)
task.logging.removeStandardErrorListener(this)
task.logging.removeStandardOutputListener(this)
}
override fun afterEvaluate(project: Project, state: ProjectState) {
project.logging.removeStandardOutputListener(this)
project.logging.removeStandardErrorListener(this)
}
override fun beforeEvaluate(project: Project) {
project.logging.addStandardOutputListener(this)
project.logging.addStandardErrorListener(this)
}
override fun graphPopulated(taskGraph: TaskExecutionGraph) {
}
override fun beforeActions(task: Task) {
task.logging.addStandardErrorListener(this)
task.logging.addStandardOutputListener(this)
}
override fun afterActions(task: Task) {
task.logging.removeStandardErrorListener(this)
task.logging.removeStandardOutputListener(this)
}
@Synchronized
fun appendText(s: String?) {
s?.split('\n')?.forEach {
if(it.replace("\n", "").trim().isNotEmpty())
file.appendText("$it\n")
}
}
@Synchronized
fun appendError(t: Throwable?) {
t?.let { error ->
ByteArrayOutputStream().let { os ->
error.printStackTrace(PrintStream(os))
String(os.toByteArray())
}.let {
appendText(it)
}
}
}
}