4
4
import hudson .console .AnnotatedLargeText ;
5
5
import hudson .util .HttpResponses ;
6
6
import io .jenkins .plugins .pipelinegraphview .utils .AbstractPipelineViewAction ;
7
+ import io .jenkins .plugins .pipelinegraphview .utils .PipelineNodeUtil ;
7
8
import io .jenkins .plugins .pipelinegraphview .utils .PipelineStepApi ;
8
9
import io .jenkins .plugins .pipelinegraphview .utils .PipelineStepList ;
9
10
import java .io .IOException ;
10
- import java .io .Writer ;
11
11
import java .util .HashMap ;
12
12
import net .sf .json .JSONObject ;
13
- import org .apache .commons .io .output .StringBuilderWriter ;
14
- import org .jenkinsci .plugins .workflow .actions .LogAction ;
15
13
import org .jenkinsci .plugins .workflow .flow .FlowExecution ;
16
14
import org .jenkinsci .plugins .workflow .graph .FlowNode ;
17
15
import org .jenkinsci .plugins .workflow .job .WorkflowRun ;
@@ -97,52 +95,65 @@ public HttpResponse getAllSteps(StaplerRequest req) throws IOException {
97
95
*/
98
96
@ GET
99
97
@ WebMethod (name = "consoleOutput" )
100
- public HttpResponse getConsolOutput (StaplerRequest req ) throws IOException {
98
+ public HttpResponse getConsoleOutput (StaplerRequest req ) throws IOException {
101
99
String nodeId = req .getParameter ("nodeId" );
102
100
if (nodeId == null ) {
103
101
logger .error ("'consoleJson' was not passed 'nodeId'." );
104
102
return HttpResponses .errorJSON ("Error getting console json" );
105
103
}
106
- // startByte to start getting data from. If negative will startByte from end of string with
107
- // LOG_THRESHOLD.
108
- Long startByte = parseIntWithDefault (req .getParameter ("startByte" ), -LOG_THRESHOLD );
109
104
logger .debug ("getConsoleOutput was passed node id '" + nodeId + "'." );
110
- Writer stringWriter = new StringBuilderWriter ();
111
- AnnotatedLargeText <? extends FlowNode > logText = getLogForNode (nodeId );
112
- HashMap <String , Object > response = new HashMap <String , Object >();
105
+
106
+ Long startByte = 0L ;
113
107
long endByte = 0L ;
114
108
long textLength = 0L ;
115
109
String text = "" ;
116
- if (logText != null ) {
117
- textLength = logText .length ();
118
- // postitive startByte
119
- if (startByte > textLength ) {
120
- // Avoid resource leak.
121
- stringWriter .close ();
122
- logger .error ("consoleJson - user requested startByte larger than console output." );
123
- return HttpResponses .errorJSON ("startByte too large." );
124
- }
125
- // if startByte is negative make sure we don't try and get a byte before 0.
126
- if (startByte < 0 ) {
127
- logger .info (
128
- "consoleJson - requested negative startByte '" + Long .toString (startByte ) + "'." );
129
- startByte = textLength + startByte ;
110
+ // If this is an exception, return the exception text (inc. stacktrace).
111
+ if (isUnhandledException (nodeId )) {
112
+ // Set logText to exception text. This is a little hacky - maybe it would be better update the
113
+ // frontend to handle steps and exceptions differently?
114
+ text = getNodeExceptionText (nodeId );
115
+ endByte = text .length ();
116
+ } else {
117
+ // This will be a step, so return it's log output.
118
+ // startByte to start getting data from. If negative will startByte from end of string with
119
+ // LOG_THRESHOLD.
120
+ startByte = parseIntWithDefault (req .getParameter ("startByte" ), -LOG_THRESHOLD );
121
+
122
+ AnnotatedLargeText <? extends FlowNode > logText = getLogForNode (nodeId );
123
+
124
+ if (logText != null ) {
125
+ textLength = logText .length ();
126
+ // postitive startByte
127
+ if (startByte > textLength ) {
128
+ // Avoid resource leak.
129
+ logger .error ("consoleJson - user requested startByte larger than console output." );
130
+ return HttpResponses .errorJSON ("startByte too large." );
131
+ }
132
+ // if startByte is negative make sure we don't try and get a byte before 0.
130
133
if (startByte < 0 ) {
131
- logger .info (
132
- "consoleJson - requested negative startByte '"
133
- + Long .toString (startByte )
134
- + "' out of bounds, setting to 0." );
135
- startByte = 0L ;
134
+ logger .debug (
135
+ "consoleJson - requested negative startByte '" + Long .toString (startByte ) + "'." );
136
+ startByte = textLength + startByte ;
137
+ if (startByte < 0 ) {
138
+ logger .debug (
139
+ "consoleJson - requested negative startByte '"
140
+ + Long .toString (startByte )
141
+ + "' out of bounds, setting to 0." );
142
+ startByte = 0L ;
143
+ }
136
144
}
145
+
146
+ logger .debug (
147
+ "Returning '"
148
+ + Long .toString (textLength - startByte )
149
+ + "' bytes from 'getConsoleOutput'." );
150
+ } else {
151
+ // If there is no text then set set startByte to 0 - as we have read from the start, there
152
+ // is just nothing there.
153
+ startByte = 0L ;
137
154
}
138
- // NOTE: This returns the total length of the console log, not the received bytes.
139
- endByte = logText .writeHtmlTo (startByte , stringWriter );
140
- text = stringWriter .toString ();
141
- logger .info (
142
- "Returning '"
143
- + Long .toString (textLength - startByte )
144
- + "' bytes from 'getConsolOutput'." );
145
155
}
156
+ HashMap <String , Object > response = new HashMap <String , Object >();
146
157
response .put ("text" , text );
147
158
response .put ("startByte" , startByte );
148
159
response .put ("endByte" , endByte );
@@ -152,25 +163,35 @@ public HttpResponse getConsolOutput(StaplerRequest req) throws IOException {
152
163
private AnnotatedLargeText <? extends FlowNode > getLogForNode (String nodeId ) throws IOException {
153
164
FlowExecution execution = target .getExecution ();
154
165
if (execution != null ) {
155
- logger .debug ("getConsoleOutput found execution." );
156
- FlowNode node = execution .getNode (nodeId );
157
- if (node != null ) {
158
- logger .debug ("getConsoleOutput found node." );
159
- LogAction log = node .getAction (LogAction .class );
160
- if (log != null ) {
161
- return log .getLogText ();
162
- }
163
- }
166
+ logger .debug ("getLogForNode found execution." );
167
+ PipelineNodeUtil .getLogText (execution .getNode (nodeId ));
164
168
}
165
169
return null ;
166
170
}
167
171
172
+ private String getNodeExceptionText (String nodeId ) throws IOException {
173
+ FlowExecution execution = target .getExecution ();
174
+ if (execution != null ) {
175
+ logger .debug ("getNodeException found execution." );
176
+ return PipelineNodeUtil .getExceptionText (execution .getNode (nodeId ));
177
+ }
178
+ return null ;
179
+ }
180
+
181
+ private boolean isUnhandledException (String nodeId ) throws IOException {
182
+ FlowExecution execution = target .getExecution ();
183
+ if (execution != null ) {
184
+ return PipelineNodeUtil .isUnhandledException (execution .getNode (nodeId ));
185
+ }
186
+ return false ;
187
+ }
188
+
168
189
private static long parseIntWithDefault (String s , long default_value ) {
169
190
try {
170
- logger .info ("Parsing user provided value of '" + s + "'" );
191
+ logger .debug ("Parsing user provided value of '" + s + "'" );
171
192
return Long .parseLong (s );
172
193
} catch (NumberFormatException e ) {
173
- logger .info ("Using default value of '" + default_value + "'" );
194
+ logger .debug ("Using default value of '" + default_value + "'" );
174
195
return default_value ;
175
196
}
176
197
}
0 commit comments