Skip to content

Commit

Permalink
Merge pull request #743 from tobyp/add-step-result-attribute
Browse files Browse the repository at this point in the history
feat: Store step/stage result as attribute on corresponding spans
  • Loading branch information
kuisathaverat authored Jan 2, 2024
2 parents 4068250 + 234423c commit a2ac52b
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 31 deletions.
1 change: 1 addition & 0 deletions docs/job-traces.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Attributes reported on the span of pipeline steps:
| jenkins.pipeline.step.name | Step name (user friendly) | String |
| jenkins.pipeline.step.type | Step name | String |
| jenkins.pipeline.step.id | Step id | String |
| jenkins.pipeline.step.result | Step result | Enum (`ABORTED`, `FAILURE`, `NOT_EXECUTED`, `PAUSED_PENDING_INPUT`, `QUEUED`, `SUCCESS`, `UNSTABLE`; see [GenericStatus](https://javadoc.jenkins.io/plugin/pipeline-graph-analysis/org/jenkinsci/plugins/workflow/pipelinegraphanalysis/GenericStatus.html)) |
| jenkins.pipeline.step.plugin.name | Jenkins plugin for that particular step | String |
| jenkins.pipeline.step.plugin.version| Jenkins plugin version | String |
| jenkins.pipeline.step.agent.label | Labels attached to the agent | String |
Expand Down
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>pipeline-stage-step</artifactId>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>pipeline-graph-analysis</artifactId>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>scm-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
import org.jenkinsci.plugins.workflow.flow.StepListener;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.pipelinegraphanalysis.GenericStatus;
import org.jenkinsci.plugins.workflow.pipelinegraphanalysis.StatusAndTiming;
import org.jenkinsci.plugins.workflow.steps.*;

import edu.umd.cs.findbugs.annotations.NonNull;
Expand Down Expand Up @@ -129,7 +131,7 @@ public void onStartNodeStep(@NonNull StepStartNode stepStartNode, @Nullable Stri
@Override
public void onAfterStartNodeStep(@NonNull StepStartNode stepStartNode, @Nullable String nodeLabel, @NonNull WorkflowRun run) {
// end the JenkinsOtelSemanticAttributes.AGENT_ALLOCATE span
endCurrentSpan(stepStartNode, run);
endCurrentSpan(stepStartNode, run, null);
}

@Override
Expand All @@ -156,13 +158,17 @@ public void onStartStageStep(@NonNull StepStartNode stepStartNode, @NonNull Stri
}

@Override
public void onEndNodeStep(@NonNull StepEndNode node, @NonNull String nodeName, @NonNull WorkflowRun run) {
endCurrentSpan(node, run);
public void onEndNodeStep(@NonNull StepEndNode node, @NonNull String nodeName, FlowNode nextNode, @NonNull WorkflowRun run) {
StepStartNode nodeStartNode = node.getStartNode();
GenericStatus nodeStatus = StatusAndTiming.computeChunkStatus2(run, null, nodeStartNode, node, nextNode);
endCurrentSpan(node, run, nodeStatus);
}

@Override
public void onEndStageStep(@NonNull StepEndNode node, @NonNull String stageName, @NonNull WorkflowRun run) {
endCurrentSpan(node, run);
public void onEndStageStep(@NonNull StepEndNode node, @NonNull String stageName, FlowNode nextNode, @NonNull WorkflowRun run) {
StepStartNode stageStartNode = node.getStartNode();
GenericStatus stageStatus = StatusAndTiming.computeChunkStatus2(run, null, stageStartNode, node, nextNode);
endCurrentSpan(node, run, stageStatus);
}

protected List<StepHandler> getStepHandlers() {
Expand Down Expand Up @@ -212,12 +218,13 @@ public void onAtomicStep(@NonNull StepAtomNode node, @NonNull WorkflowRun run) {
}

@Override
public void onAfterAtomicStep(@NonNull StepAtomNode node, @NonNull WorkflowRun run) {
public void onAfterAtomicStep(@NonNull StepAtomNode node, FlowNode nextNode, @NonNull WorkflowRun run) {
if (isIgnoredStep(node.getDescriptor())){
LOGGER.log(Level.FINE, () -> run.getFullDisplayName() + " - don't end span for step '" + node.getDisplayFunctionName() + "'");
return;
}
endCurrentSpan(node, run);
GenericStatus stageStatus = StatusAndTiming.computeChunkStatus2(run, null, node, node, nextNode);
endCurrentSpan(node, run, stageStatus);
}

private boolean isIgnoredStep(@Nullable StepDescriptor stepDescriptor) {
Expand Down Expand Up @@ -289,24 +296,30 @@ public void onStartParallelStepBranch(@NonNull StepStartNode stepStartNode, @Non
}

@Override
public void onEndParallelStepBranch(@NonNull StepEndNode node, @NonNull String branchName, @NonNull WorkflowRun run) {
endCurrentSpan(node, run);
public void onEndParallelStepBranch(@NonNull StepEndNode node, @NonNull String branchName, FlowNode nextNode, @NonNull WorkflowRun run) {
StepStartNode parallelStartNode = node.getStartNode();
GenericStatus parallelStatus = StatusAndTiming.computeChunkStatus2(run, null, parallelStartNode, node, nextNode);
endCurrentSpan(node, run, parallelStatus);
}

private void endCurrentSpan(FlowNode node, WorkflowRun run) {
private void endCurrentSpan(FlowNode node, WorkflowRun run, GenericStatus status) {
try (Scope ignored = setupContext(run, node)) {
verifyNotNull(ignored, "%s - No span found for node %s", run, node);

Span span = getTracerService().getSpan(run, node);

ErrorAction errorAction = node.getError();
if (errorAction == null) {
if (status == null) status = GenericStatus.SUCCESS;
span.setStatus(StatusCode.OK);
} else {
Throwable throwable = errorAction.getError();
if (throwable instanceof FlowInterruptedException) {
FlowInterruptedException interruptedException = (FlowInterruptedException) throwable;
List<CauseOfInterruption> causesOfInterruption = interruptedException.getCauses();

if (status == null) status = GenericStatus.fromResult(interruptedException.getResult());

Check warning on line 321 in src/main/java/io/jenkins/plugins/opentelemetry/job/MonitoringPipelineListener.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 321 is only partially covered, one branch is missing

List<String> causeDescriptions = causesOfInterruption.stream().map(cause -> cause.getClass().getSimpleName() + ": " + cause.getShortDescription()).collect(Collectors.toList());
span.setAttribute(JenkinsOtelSemanticAttributes.JENKINS_STEP_INTERRUPTION_CAUSES, causeDescriptions);

Expand All @@ -326,10 +339,17 @@ private void endCurrentSpan(FlowNode node, WorkflowRun run) {
span.setStatus(StatusCode.ERROR, statusDescription);
}
} else {
if (status == null) status = GenericStatus.FAILURE;

Check warning on line 342 in src/main/java/io/jenkins/plugins/opentelemetry/job/MonitoringPipelineListener.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 342 is only partially covered, one branch is missing
span.recordException(throwable);
span.setStatus(StatusCode.ERROR, throwable.getMessage());
}
}

if (status != null) {

Check warning on line 348 in src/main/java/io/jenkins/plugins/opentelemetry/job/MonitoringPipelineListener.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 348 is only partially covered, one branch is missing
status = StatusAndTiming.coerceStatusApi(status, StatusAndTiming.API_V2);
span.setAttribute(JenkinsOtelSemanticAttributes.JENKINS_STEP_RESULT, status.toString());
}

span.end();
LOGGER.log(Level.FINE, () -> run.getFullDisplayName() + " - < " + node.getDisplayFunctionName() + " - end " + OtelUtils.toDebugString(span));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ public void onAfterStartNodeStep(@NonNull StepStartNode stepStartNode, @Nullable
}

@Override
public void onEndNodeStep(@NonNull StepEndNode nodeStepEndNode, @NonNull String nodeName, @NonNull WorkflowRun run) {
public void onEndNodeStep(@NonNull StepEndNode nodeStepEndNode, @NonNull String nodeName, FlowNode nextNode, @NonNull WorkflowRun run) {

}

@Override
public void onEndStageStep(@NonNull StepEndNode stageStepEndNode, @NonNull String stageName, @NonNull WorkflowRun run) {
public void onEndStageStep(@NonNull StepEndNode stageStepEndNode, @NonNull String stageName, FlowNode nextNode, @NonNull WorkflowRun run) {

}

Expand All @@ -51,7 +51,7 @@ public void onAtomicStep(@NonNull StepAtomNode node, @NonNull WorkflowRun run) {
}

@Override
public void onAfterAtomicStep(@NonNull StepAtomNode stepAtomNode, @NonNull WorkflowRun run) {
public void onAfterAtomicStep(@NonNull StepAtomNode stepAtomNode, FlowNode nextNode, @NonNull WorkflowRun run) {

}

Expand All @@ -61,7 +61,7 @@ public void onStartParallelStepBranch(@NonNull StepStartNode stepStartNode, @Non
}

@Override
public void onEndParallelStepBranch(@NonNull StepEndNode stepStepNode, @NonNull String branchName, @NonNull WorkflowRun run) {
public void onEndParallelStepBranch(@NonNull StepEndNode stepStepNode, @NonNull String branchName, FlowNode nextNode, @NonNull WorkflowRun run) {

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,18 @@ private void processPreviousNodes(FlowNode node, WorkflowRun run) {
log(Level.FINE, () -> run.getFullDisplayName() + " - Process previous node " + PipelineNodeUtil.getDetailedDebugString(previousNode) + " of node " + PipelineNodeUtil.getDetailedDebugString(node));
if (previousNode instanceof StepAtomNode) {
StepAtomNode stepAtomNode = (StepAtomNode) previousNode;
fireOnAfterAtomicStep(stepAtomNode, run);
fireOnAfterAtomicStep(stepAtomNode, node, run);
} else if (isBeforeEndExecutorNodeStep(previousNode)) {
String nodeName = PipelineNodeUtil.getDisplayName(((StepEndNode) previousNode).getStartNode());
fireOnAfterEndNodeStep((StepEndNode) previousNode, nodeName, run);
fireOnAfterEndNodeStep((StepEndNode) previousNode, nodeName, node, run);
} else if (isBeforeEndStageStep(previousNode)) {
String stageName = PipelineNodeUtil.getDisplayName(((StepEndNode) previousNode).getStartNode());
fireOnAfterEndStageStep((StepEndNode) previousNode, stageName, run);
fireOnAfterEndStageStep((StepEndNode) previousNode, stageName, node, run);
} else if (isBeforeEndParallelBranch(previousNode)) {
StepEndNode endParallelBranchNode = (StepEndNode) previousNode;
StepStartNode beginParallelBranch = endParallelBranchNode.getStartNode();
ThreadNameAction persistentAction = verifyNotNull(beginParallelBranch.getPersistentAction(ThreadNameAction.class), "Null ThreadNameAction on %s", beginParallelBranch);
fireOnAfterEndParallelStepBranch(endParallelBranchNode, persistentAction.getThreadName(), run);
fireOnAfterEndParallelStepBranch(endParallelBranchNode, persistentAction.getThreadName(), node, run);
} else {
log(Level.FINE, () -> "Ignore previous node " + PipelineNodeUtil.getDetailedDebugString(previousNode));
}
Expand Down Expand Up @@ -129,11 +129,11 @@ private void fireOnBeforeStartParallelStepBranch(@NonNull StepStartNode node, @N
}
}

private void fireOnAfterEndParallelStepBranch(@NonNull StepEndNode node, @NonNull String branchName, @NonNull WorkflowRun run) {
private void fireOnAfterEndParallelStepBranch(@NonNull StepEndNode node, @NonNull String branchName, FlowNode nextNode, @NonNull WorkflowRun run) {
for (PipelineListener pipelineListener : PipelineListener.all()) {
log(Level.FINE, () -> "onAfterEndParallelStepBranch(branchName: " + branchName + ", node[name:" + node.getDisplayName() + ", id: " + node.getId() + "]): " + pipelineListener.toString());
try {
pipelineListener.onEndParallelStepBranch(node, branchName, run);
pipelineListener.onEndParallelStepBranch(node, branchName, nextNode, run);
} catch (RuntimeException e) {
LOGGER.log(Level.WARNING, e, () -> "Exception invoking `onAfterEndParallelStepBranch` on " + pipelineListener);
}
Expand All @@ -157,11 +157,11 @@ private void logFlowNodeDetails(@NonNull FlowNode node, @NonNull WorkflowRun run
});
}

public void fireOnAfterAtomicStep(@NonNull StepAtomNode stepAtomNode, @NonNull WorkflowRun run) {
public void fireOnAfterAtomicStep(@NonNull StepAtomNode stepAtomNode, FlowNode nextNode, @NonNull WorkflowRun run) {
for (PipelineListener pipelineListener : PipelineListener.all()) {
log(() -> "onAfterAtomicStep(" + stepAtomNode.getDisplayName() + "): " + pipelineListener.toString());
try {
pipelineListener.onAfterAtomicStep(stepAtomNode, run);
pipelineListener.onAfterAtomicStep(stepAtomNode, nextNode, run);
} catch (RuntimeException e) {
LOGGER.log(Level.WARNING, e, () -> "Exception invoking `onAfterAtomicStep` on " + pipelineListener);
}
Expand Down Expand Up @@ -190,22 +190,22 @@ public void fireOnStartPipeline(@NonNull FlowStartNode node, @NonNull WorkflowRu
}
}

public void fireOnAfterEndNodeStep(@NonNull StepEndNode node, @NonNull String nodeName, @NonNull WorkflowRun run) {
public void fireOnAfterEndNodeStep(@NonNull StepEndNode node, @NonNull String nodeName, FlowNode nextNode, @NonNull WorkflowRun run) {
for (PipelineListener pipelineListener : PipelineListener.all()) {
log(() -> "onAfterEndNodeStep(" + node.getDisplayName() + "): " + pipelineListener.toString() + (node.getError() != null ? ("error: " + node.getError().getError()) : ""));
try {
pipelineListener.onEndNodeStep(node, nodeName, run);
pipelineListener.onEndNodeStep(node, nodeName, nextNode, run);
} catch (RuntimeException e) {
LOGGER.log(Level.WARNING, e, () -> "Exception invoking `onAfterEndNodeStep` on " + pipelineListener);
}
}
}

public void fireOnAfterEndStageStep(@NonNull StepEndNode node, @NonNull String stageName, @NonNull WorkflowRun run) {
public void fireOnAfterEndStageStep(@NonNull StepEndNode node, @NonNull String stageName, FlowNode nextNode, @NonNull WorkflowRun run) {
for (PipelineListener pipelineListener : PipelineListener.all()) {
log(() -> "onAfterEndStageStep(" + node.getDisplayName() + "): " + pipelineListener.toString() + (node.getError() != null ? ("error: " + node.getError().getError()) : ""));
try {
pipelineListener.onEndStageStep(node, stageName, run);
pipelineListener.onEndStageStep(node, stageName, nextNode, run);
} catch (RuntimeException e) {
LOGGER.log(Level.WARNING, e, () -> "Exception invoking `onAfterEndStageStep` on " + pipelineListener);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ static List<PipelineListener> all() {
/**
* Just after the `node` step ends
*/
void onEndNodeStep(@NonNull StepEndNode nodeStepEndNode, @NonNull String nodeName, @NonNull WorkflowRun run);
void onEndNodeStep(@NonNull StepEndNode nodeStepEndNode, @NonNull String nodeName, FlowNode nextNode, @NonNull WorkflowRun run);

/**
* Just before the `stage`step starts
Expand All @@ -51,7 +51,7 @@ static List<PipelineListener> all() {
/**
* Just after the `stage` step ends
*/
void onEndStageStep(@NonNull StepEndNode stageStepEndNode, @NonNull String stageName, @NonNull WorkflowRun run);
void onEndStageStep(@NonNull StepEndNode stageStepEndNode, @NonNull String stageName, FlowNode nextNode, @NonNull WorkflowRun run);

/**
* Just before the `parallel` branch starts
Expand All @@ -61,7 +61,7 @@ static List<PipelineListener> all() {
/**
* Just before the `parallel` branch ends
*/
void onEndParallelStepBranch(@NonNull StepEndNode stepStepNode, @NonNull String branchName, @NonNull WorkflowRun run);
void onEndParallelStepBranch(@NonNull StepEndNode stepStepNode, @NonNull String branchName, FlowNode nextNode, @NonNull WorkflowRun run);

/**
* Just before the atomic step starts
Expand All @@ -71,7 +71,7 @@ static List<PipelineListener> all() {
/**
* Just after the atomic step
*/
void onAfterAtomicStep(@NonNull StepAtomNode stepAtomNode, @NonNull WorkflowRun run);
void onAfterAtomicStep(@NonNull StepAtomNode stepAtomNode, FlowNode nextNode, @NonNull WorkflowRun run);

/**
* Just after the pipeline ends
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ public final class JenkinsOtelSemanticAttributes {
* @see org.jenkinsci.plugins.workflow.graph.FlowNode#getId()
*/
public static final AttributeKey<String> JENKINS_STEP_ID = AttributeKey.stringKey("jenkins.pipeline.step.id");
/**
* @see org.jenkinsci.plugins.workflow.pipelinegraphanalysis.GenericStatus
* @see org.jenkinsci.plugins.workflow.pipelinegraphanalysis.StatusAndTiming#computeChunkStatus2(org.jenkinsci.plugins.workflow.job.WorkflowRun,org.jenkinsci.plugins.workflow.graph.FlowNode,org.jenkinsci.plugins.workflow.graph.FlowNode,org.jenkinsci.plugins.workflow.graph.FlowNode,org.jenkinsci.plugins.workflow.graph.FlowNode)
*/
public static final AttributeKey<String> JENKINS_STEP_RESULT = AttributeKey.stringKey("jenkins.pipeline.step.result");
/**
* @see PluginWrapper#getShortName()
*/
Expand All @@ -97,7 +102,7 @@ public final class JenkinsOtelSemanticAttributes {
public static final String JENKINS = "jenkins";

/**
* As {@link Jenkins.MasterComputer#getName()} return "", choose another name
* As {@link Jenkins.MasterComputer#getName()} returns "", choose another name
*
* @see Jenkins.MasterComputer#getName()
*/
Expand Down
Loading

0 comments on commit a2ac52b

Please sign in to comment.