Skip to content

Commit e75c614

Browse files
committed
show config info in bpipe dev output, and display tail output from failed commands at end of pipeline
1 parent b90a101 commit e75c614

File tree

7 files changed

+76
-26
lines changed

7 files changed

+76
-26
lines changed

src/main/groovy/bpipe/Command.groovy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ class Command implements Serializable {
116116
*/
117117
List<String> shell
118118

119+
transient OutputLog outputLog
120+
119121
File dir
120122

121123
transient List<CommandDependency> dependencies

src/main/groovy/bpipe/CommandFailedException.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,25 @@
3333
*/
3434
class CommandFailedException extends PipelineError {
3535

36-
Command command;
36+
private static final long serialVersionUID = 1L;
37+
38+
private Command command;
3739

3840
public CommandFailedException(String arg0, PipelineContext ctx, Command command) {
3941
super(arg0, ctx);
40-
this.command = command;
42+
this.setCommand(command);
4143
}
4244

4345
public CommandFailedException(Throwable arg0) {
4446
super(arg0);
4547
}
48+
49+
public Command getCommand() {
50+
return command;
51+
}
52+
53+
public void setCommand(Command command) {
54+
this.command = command;
55+
}
4656

4757
}

src/main/groovy/bpipe/CommandManager.groovy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ class CommandManager {
151151

152152
command.name = name
153153
command.executor = wrapped
154+
command.outputLog = commandLog
155+
154156
wrapped.start(cfg, command, commandLog, commandLog)
155157

156158
this.commandIds[cmdExec] = command.id

src/main/groovy/bpipe/OutputLog.groovy

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ class OutputLog implements Appendable {
5757
StringBuilder buffer = new StringBuilder()
5858

5959
// New fields for maintaining a rolling tail of the last N lines
60-
int maxTailLines = 5
61-
List<String> tailBuffer = []
60+
int maxTailLines = 5
61+
62+
ArrayDeque<String> tailBuffer = new ArrayDeque(maxTailLines)
6263

6364
String branch
6465
String prefix
@@ -90,7 +91,7 @@ class OutputLog implements Appendable {
9091
* @param output
9192
*/
9293
void buffer(CharSequence output) {
93-
output.eachLine { String line ->
94+
output.eachLine { String line ->
9495
bufferLine(line)
9596
}
9697
}
@@ -112,11 +113,12 @@ class OutputLog implements Appendable {
112113

113114
}
114115

115-
void bufferLine(String line) {
116-
// Update rolling tail buffer
117-
tailBuffer << line
118-
if(tailBuffer.size() > maxTailLines) {
119-
tailBuffer.remove(0)
116+
void bufferLine(String line) {
117+
118+
// Update rolling tail buffer
119+
tailBuffer.addLast(line)
120+
if(tailBuffer.size() > maxTailLines) {
121+
tailBuffer.removeFirst()
120122
}
121123

122124
// In dev mode, no output log at all, just print raw output
@@ -157,9 +159,10 @@ class OutputLog implements Appendable {
157159
buffer.append(arg0,arg1,arg2)
158160
this.flush()
159161
return this;
160-
}
161-
List<String> getLastTailLines() {
162-
return tailBuffer.clone()
162+
}
163+
164+
Iterable<String> getLastTailLines() {
165+
return tailBuffer
163166
}
164167
}
165168

src/main/groovy/bpipe/Pipeline.groovy

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import org.codehaus.groovy.runtime.ReverseListIterator
5353
import bpipe.graph.Graph;
5454
import bpipe.storage.StorageLayer
5555
import bpipe.storage.UnknownStoragePipelineFile
56+
import static org.fusesource.jansi.Ansi.*
5657

5758
import static bpipe.Utils.isContainer
5859
import static bpipe.Utils.unbox
@@ -930,14 +931,27 @@ public class Pipeline implements ResourceRequestor {
930931
String branchPart = pe.ctx?.branch?.firstNonTrivialName ? " ($pe.ctx.branch.firstNonTrivialName) " : ""
931932
String stageName = pe.ctx?.stageName
932933
if(stageName && stageName != "Unknown") {
933-
return "In stage ${pe.ctx?.stageName}$branchPart: " + pe.message
934+
String mainMessage = "In stage ${pe.ctx?.stageName}$branchPart: " + pe.message
935+
936+
if(pe instanceof CommandFailedException) {
937+
OutputLog o = pe.getCommand().outputLog
938+
if(o?.getLastTailLines()) {
939+
mainMessage += "\n\nTrailing lines:${ansi().fgRed()}\n\n " + o.getLastTailLines().join('\n ') + ansi().fgDefault() + '\n'
940+
}
941+
else {
942+
mainMessage += "\nNo output recorded from command\n"
943+
}
944+
}
945+
946+
return mainMessage
934947
}
935948
else {
936949
return pe.message
937950
}
938951
}
939-
else
952+
else {
940953
return e.message
954+
}
941955
}.join('\n')
942956
}
943957

src/main/groovy/bpipe/PipelineContext.groovy

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2091,7 +2091,13 @@ class PipelineContext {
20912091
String threadsMsg = ''
20922092
if(prettyCmd.contains('__bpipe_lazy_resource_threads__')) {
20932093
prettyCmd = prettyCmd.replaceAll('__bpipe_lazy_resource_threads__', "${ansi().fgGreen()}\\\${threads}${ansi().fgDefault()}")
2094-
threadsMsg = "${ansi().bold()}Note:${ansi().boldOff()} ${ansi().fgGreen()}\${threads}${ansi().fgDefault()} will be assigned at runtime based on available concurrency"
2094+
threadsMsg = "${ansi().bold()}Note:${ansi().boldOff()} ${ansi().fgGreen()}\${threads}${ansi().fgDefault()} will be assigned at runtime based on available concurrency\n"
2095+
}
2096+
2097+
String memoryMsg = ""
2098+
if(prettyCmd.contains('__bpipe_lazy_resource_memory__')) {
2099+
prettyCmd = prettyCmd.replaceAll('__bpipe_lazy_resource_memory__', "${ansi().fgGreen()}\\\${memory}${ansi().fgDefault()}")
2100+
memoryMsg = "${ansi().bold()}Note:${ansi().boldOff()} ${ansi().fgGreen()}\${memory}${ansi().fgDefault()} will be assigned based on configuration\n"
20952101
}
20962102

20972103
String msg = branch.name ? "Stage $stageName in branch $branch.name would execute:\n\n $prettyCmd" : "Would execute $prettyCmd"
@@ -2107,7 +2113,7 @@ class PipelineContext {
21072113
if(configObject?.containsKey('container') && configObject.container) {
21082114
ConfigObject container = configObject['container']
21092115
if(container)
2110-
containerMsg = "Will execute in container: " + container + condaInfo
2116+
containerMsg = "Will execute in container: " + container + condaInfo + '\n'
21112117
}
21122118
else {
21132119
if(condaInfo)
@@ -2117,20 +2123,20 @@ class PipelineContext {
21172123
println msg
21182124

21192125
println threadsMsg
2126+
2127+
if(memoryMsg)
2128+
println memoryMsg
21202129

21212130
if(containerMsg)
21222131
println containerMsg
2123-
String memoryMsg = ""
2124-
if(prettyCmd.contains('__bpipe_lazy_resource_memory__')) {
2125-
prettyCmd = prettyCmd.replaceAll('__bpipe_lazy_resource_memory__', "${ansi().fgGreen()}\\\${memory}${ansi().fgDefault()}")
2126-
memoryMsg = "${ansi().bold()}Note:${ansi().boldOff()} ${ansi().fgGreen()}\${memory}${ansi().fgDefault()} will be assigned at runtime"
2132+
2133+
if(config) {
2134+
println("Config".padRight(12) + ": " + config)
21272135
}
2128-
if(memoryMsg)
2129-
println memoryMsg
21302136

2131-
for(key in ['Executor','Memory','Walltime','Modules']) {
2137+
for(key in ['Executor','Queue','Memory','Walltime','Modules']) {
21322138
if(configObject.containsKey(key.toLowerCase()))
2133-
println((key.padRight(12)) + ": " + configObject[key.toLowerCase()])
2139+
println((key.take(11).padRight(12)) + ": " + configObject[key.toLowerCase()])
21342140
}
21352141

21362142
println "\n${ansi().fgBlue()}Waiting for changes or <enter> to continue ....${ansi().fgDefault()}\n"
@@ -2153,7 +2159,8 @@ class PipelineContext {
21532159

21542160
log.info "Command $c.id in branch $branch failed with exit code $c.exitCode"
21552161

2156-
throw new CommandFailedException("Command $c.id in stage $stageName failed with exit status = $c.exitCode : \n\n$c.command", this, c)
2162+
String message = "Command $c.id in stage $stageName failed with exit status = $c.exitCode : \n\n$c.command"
2163+
throw new CommandFailedException(message, this, c)
21572164
}
21582165

21592166
if(!this.probeMode)

src/main/groovy/bpipe/Utils.groovy

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import java.util.logging.Level;
4848
import java.util.logging.Logger;
4949
import java.util.regex.Pattern
5050
import org.codehaus.groovy.runtime.StackTraceUtils
51+
import static org.fusesource.jansi.Ansi.*
5152

5253
import bpipe.storage.StorageLayer
5354

@@ -1097,6 +1098,17 @@ class Utils {
10971098

10981099
String result = (e.ctx ? " $e.ctx.stageName $branch " : "").center(width,"-") + "\n\n" + e.message + "\n"
10991100

1101+
if(e instanceof CommandFailedException) {
1102+
OutputLog o = e.getCommand().outputLog
1103+
if(o?.getLastTailLines()) {
1104+
result += "\n\nTrailing lines:${ansi().fgRed()}\n\n " + o.getLastTailLines().join('\n ') + ansi().fgDefault() + '\n'
1105+
}
1106+
else {
1107+
result += "\nNo output recorded from command\n"
1108+
}
1109+
}
1110+
1111+
11001112
// if(branchHierarchy) {
11011113
// result = result + "\n$branchInfo\n"
11021114
// }

0 commit comments

Comments
 (0)