Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added step withSpanAttributes to set the attributes also on child spans, added setSpanAttributes for setting the attribute only on the target #827

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
0a05828
173 Add parameter setOn to the withSpanAttribute step (WIP)
christophe-kamphaus-jemmic Mar 14, 2024
69d596a
173 Rename + improve existing test case testSimplePipelineWithWithSpa…
christophe-kamphaus-jemmic Mar 14, 2024
aa9775a
173 Added test case for setOn parameter testSimplePipelineWithWithSpa…
christophe-kamphaus-jemmic Mar 14, 2024
2da15a2
173 Added asserts for setOn test case
christophe-kamphaus-jemmic Mar 14, 2024
ea01c45
173 Use assert reason instead of comments
christophe-kamphaus-jemmic Mar 14, 2024
34104af
173 Implement attribute setting on child nodes
christophe-kamphaus-jemmic Mar 14, 2024
9173cd8
173 Enable test for child attributes
christophe-kamphaus-jemmic Mar 14, 2024
e953800
174 Assert attribute on phases
christophe-kamphaus-jemmic Mar 18, 2024
97d407d
173 Added assert for closed child span
christophe-kamphaus-jemmic Mar 18, 2024
dbac5f9
173 Add test for block use of withSpanAttribute
christophe-kamphaus-jemmic Mar 18, 2024
264344a
173 Add test for checking overridden attribute keys
christophe-kamphaus-jemmic Mar 18, 2024
95c1ace
173 Add comment about overriding attributes
christophe-kamphaus-jemmic Mar 18, 2024
7df888f
173 Fix error "Failed to serialize AttributeSetterAction#attributeKey"
christophe-kamphaus-jemmic Mar 18, 2024
e4d3464
173 Improve comment
christophe-kamphaus-jemmic Mar 18, 2024
5db2e48
173 Improved logging
christophe-kamphaus-jemmic Mar 18, 2024
6a4eabf
173 Code style
christophe-kamphaus-jemmic Mar 18, 2024
ee7e1a3
173 Only use single action for setting span attributes
christophe-kamphaus-jemmic Mar 26, 2024
563d354
173 Code style
christophe-kamphaus-jemmic Mar 26, 2024
9acb9fc
173 Split withAttribute into withSpanAttribute and setSpanAttribute
christophe-kamphaus-jemmic Mar 26, 2024
68bb552
173 Small code style fixes
christophe-kamphaus-jemmic Mar 28, 2024
d14e9a6
173 Adapt previous test to use withSpanAttribute closure and setSpanA…
christophe-kamphaus-jemmic Mar 28, 2024
812841f
173 Add setSpanAttribute to the list of ignored steps
christophe-kamphaus-jemmic Mar 28, 2024
250c1e0
173 Remove unnecessary use of the Run's OpenTelemetryAttributesAction
christophe-kamphaus-jemmic Mar 28, 2024
b7023f9
173 Improve test method name and some comments
christophe-kamphaus-jemmic Mar 28, 2024
b511c36
173 Fix attribute override missing from agent spans
christophe-kamphaus-jemmic Mar 28, 2024
3b91c83
173 Adapt override test for setSpanAttribute and withSpanAttribute steps
christophe-kamphaus-jemmic Mar 28, 2024
851d383
173 Fix override from setSpanAttribute not working
christophe-kamphaus-jemmic Mar 28, 2024
9918379
173 Add tests for declarative pipeline
christophe-kamphaus-jemmic Mar 28, 2024
0d7e29a
173 Fix SpotBugs
christophe-kamphaus-jemmic Mar 28, 2024
0d1e9b6
173 SpotBugs fix
christophe-kamphaus-jemmic Mar 28, 2024
6b2a8fc
173 Add step withSpanAttributes
christophe-kamphaus-jemmic Mar 29, 2024
740941a
173 Allow specifying multiple span attributes
christophe-kamphaus-jemmic Apr 4, 2024
5c5094b
173 Improve error message on null value
christophe-kamphaus-jemmic Apr 4, 2024
4d30816
173 change todo comment, this has been implemented
christophe-kamphaus-jemmic Apr 18, 2024
c67b4b8
173 Change setSpanAttribute to setSpanAttributes and revert withSpanA…
christophe-kamphaus-jemmic Apr 18, 2024
b8cff39
173 Document the new spanAttribute steps
christophe-kamphaus-jemmic Apr 18, 2024
ef8641a
173 Remove unused import
christophe-kamphaus-jemmic Apr 18, 2024
4af7ffb
173 Improve comment
christophe-kamphaus-jemmic Apr 18, 2024
9d20436
173 Fix SpotBugs issue
christophe-kamphaus-jemmic Apr 18, 2024
bddf07e
173 Revert change to mark withSpanAttribute as deprecated
christophe-kamphaus-jemmic Apr 18, 2024
c26ed95
173 Add comments
christophe-kamphaus-jemmic Apr 22, 2024
d199e7a
Merge branch 'main' into 173-set-attributes-on-child-spans
christophe-kamphaus-jemmic Apr 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions docs/job-traces.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ The attributes of the traces and spans are normalized in order

### Enriching pipeline build traces with custom span attributes

[Attributes](https://opentelemetry.io/docs/reference/specification/common/#attribute) can be added to the spans associated with pipeline steps using the `withSpanAttribute(key, value[, type]) ` step.
[Attributes](https://opentelemetry.io/docs/reference/specification/common/#attribute) can be added to the spans associated with pipeline steps using one of the following pipeline steps:
* `withSpanAttribute(key, value[, type][, target])`
* `setSpanAttributes([spanAttribute(key, value[, type][, target])])`
* `withSpanAttributes([spanAttribute(key, value[, type][, target])])`

Example:

Expand All @@ -21,6 +24,10 @@ withSpanAttribute(key: "pipeline_type", value: "release_pipeline", target: "PIPE
...
stage ("build") {
withSpanAttribute(key: "build.tool", value: "maven")
setSpanAttributes([spanAttribute(key: "build.tool", value: "maven")])
withSpanAttributes([spanAttribute(key: "test.tool", value: "junit")]) {
sh "./run-tests.sh"
}
}
````

Expand All @@ -30,7 +37,26 @@ If not specified, the `type` of the attribute is inferred from the passed `value
* `CURRENT_SPAN` (default value): the attribute is set on the span of the current pipeline phase (current node, stage, step...)
* `PIPELINE_ROOT_SPAN`: the attribute is set on the root span of the pipeline (ie the "BUILD ..." span)

Note that the `withSpanAttribute` doesn't create a span in the pipeline build trace.
Note that none of the steps `setSpanAttributes`, `withSpanAttribute`, `withSpanAttributes` create a span in the pipeline build trace.

`withSpanAttribute` and `setSpanAttributes` set the given attribute(s) on the target.

`withSpanAttributes` sets the given attribute(s) on child spans.

It's also possible to use `withSpanAttributes` in declarative pipelines:

````groovy
pipeline {
options {
withSpanAttributes([spanAttribute(key: 'pipeline.type', value: 'release', target: 'PIPELINE_ROOT_SPAN')])
}
stages {
stage('build') {
...
}
}
}
````

### Pipeline, freestyle, and matrix project build spans

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,28 @@
import io.opentelemetry.api.common.AttributeKey;

import edu.umd.cs.findbugs.annotations.NonNull;

import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;

/**
* @see io.opentelemetry.api.common.AttributeKey
* @see io.opentelemetry.api.common.AttributeType
*/
public class OpenTelemetryAttributesAction extends InvisibleAction {
public class OpenTelemetryAttributesAction extends InvisibleAction implements Serializable {
private final static Logger LOGGER = Logger.getLogger(MonitoringAction.class.getName());

private static final long serialVersionUID = 5488506456727905116L;

private transient Map<AttributeKey<?>, Object> attributes;

private transient Set<String> appliedToSpans;

@NonNull
public Map<AttributeKey<?>, Object> getAttributes() {
if (attributes == null) {
Expand All @@ -32,6 +40,18 @@ public Map<AttributeKey<?>, Object> getAttributes() {
return attributes;
}

/**
* Remember a span to which these attributes are applied.
* @param spanId
* @return true iff a span did not previously have these attributes applied
*/
public boolean isNotYetAppliedToSpan(String spanId) {
if (appliedToSpans == null) {
appliedToSpans = new HashSet<>();
}
return appliedToSpans.add(spanId);
}

@Override
public String toString() {
return "OpenTelemetryAttributesAction{" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
import io.jenkins.plugins.opentelemetry.OtelUtils;
import io.jenkins.plugins.opentelemetry.job.jenkins.AbstractPipelineListener;
import io.jenkins.plugins.opentelemetry.job.jenkins.PipelineListener;
import io.jenkins.plugins.opentelemetry.job.step.SetSpanAttributesStep;
import io.jenkins.plugins.opentelemetry.job.step.StepHandler;
import io.jenkins.plugins.opentelemetry.job.step.WithSpanAttributeStep;
import io.jenkins.plugins.opentelemetry.job.step.WithSpanAttributesStep;
import io.jenkins.plugins.opentelemetry.semconv.JenkinsOtelSemanticAttributes;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.events.EventEmitter;
Expand Down Expand Up @@ -106,7 +108,7 @@

LOGGER.log(Level.FINE, () -> run.getFullDisplayName() + " - > " + JenkinsOtelSemanticAttributes.AGENT + "(" + agentLabel + ") - begin " + OtelUtils.toDebugString(agentSpan));

getTracerService().putSpan(run, agentSpan, stepStartNode);
getTracerService().putAgentSpan(run, agentSpan, stepStartNode);

try (Scope allocateAgentSpanScope = agentSpan.makeCurrent()) {
SpanBuilder allocateAgentSpanBuilder = getTracer().spanBuilder(JenkinsOtelSemanticAttributes.AGENT_ALLOCATION_UI)
Expand All @@ -123,7 +125,7 @@

LOGGER.log(Level.FINE, () -> run.getFullDisplayName() + " - > " + JenkinsOtelSemanticAttributes.AGENT_ALLOCATE + "(" + agentLabel + ") - begin " + OtelUtils.toDebugString(allocateAgentSpan));

getTracerService().putSpan(run, allocateAgentSpan, stepStartNode);
getTracerService().putAgentSpan(run, allocateAgentSpan, stepStartNode);
}
}
}
Expand Down Expand Up @@ -232,7 +234,10 @@
return true;
}
String stepFunctionName = stepDescriptor.getFunctionName();
boolean ignoreStep = WithSpanAttributeStep.DescriptorImpl.FUNCTION_NAME.equals(stepFunctionName) || this.ignoredSteps.contains(stepFunctionName);
boolean ignoreStep = SetSpanAttributesStep.DescriptorImpl.FUNCTION_NAME.equals(stepFunctionName)
|| WithSpanAttributeStep.DescriptorImpl.FUNCTION_NAME.equals(stepFunctionName)
|| WithSpanAttributesStep.DescriptorImpl.FUNCTION_NAME.equals(stepFunctionName)

Check warning on line 239 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 239 is only partially covered, one branch is missing
|| this.ignoredSteps.contains(stepFunctionName);
LOGGER.log(Level.FINER, ()-> "isIgnoreStep(" + stepDescriptor + "): " + ignoreStep);
return ignoreStep;
}
Expand Down Expand Up @@ -379,22 +384,38 @@
openTelemetryAttributesAction.getAttributes().put(AttributeKey.stringKey(JenkinsOtelSemanticAttributes.JENKINS_COMPUTER_NAME.getKey()), computer.getName());
computer.addAction(openTelemetryAttributesAction);
}
OpenTelemetryAttributesAction openTelemetryAttributesAction = computer.getAction(OpenTelemetryAttributesAction.class);
OpenTelemetryAttributesAction otelComputerAttributesAction = computer.getAction(OpenTelemetryAttributesAction.class);
OpenTelemetryAttributesAction otelChildAttributesAction = context.get(OpenTelemetryAttributesAction.class);

try (Scope ignored = setupContext(run, node)) {
Span currentSpan = Span.current();
LOGGER.log(Level.FINE, () -> "Add resource attributes to span " + OtelUtils.toDebugString(currentSpan) + " - " + openTelemetryAttributesAction);
for (Map.Entry<AttributeKey<?>, Object> entry : openTelemetryAttributesAction.getAttributes().entrySet()) {
AttributeKey<?> attributeKey = entry.getKey();
Object value = verifyNotNull(entry.getValue());
currentSpan.setAttribute((AttributeKey<? super Object>) attributeKey, value);
}
LOGGER.log(Level.FINE, () -> "Add resource attributes to span " + OtelUtils.toDebugString(currentSpan) + " - " + otelComputerAttributesAction);
setAttributesToSpan(currentSpan, otelComputerAttributesAction);

LOGGER.log(Level.FINE, () -> "Add attributes to child span " + OtelUtils.toDebugString(currentSpan) + " - " + otelChildAttributesAction);
setAttributesToSpan(currentSpan, otelChildAttributesAction);
}
} catch (IOException | InterruptedException | RuntimeException e) {
LOGGER.log(Level.WARNING,"Exception processing " + step + " - " + context, e);
}
}

private void setAttributesToSpan(@NonNull Span span, OpenTelemetryAttributesAction openTelemetryAttributesAction) {
if (openTelemetryAttributesAction == null) {
return;
}
if (!openTelemetryAttributesAction.isNotYetAppliedToSpan(span.getSpanContext().getSpanId())) {
// Do not reapply attributes, if previously applied.
// This is important for overriding of attributes to work in an intuitive manner.
return;
}
for (Map.Entry<AttributeKey<?>, Object> entry : openTelemetryAttributesAction.getAttributes().entrySet()) {
AttributeKey<?> attributeKey = entry.getKey();
Object value = verifyNotNull(entry.getValue());
span.setAttribute((AttributeKey<? super Object>) attributeKey, value);
}
}

/**
* @return {@code null} if no {@link Span} has been created for the {@link Run} of the given {@link FlowNode}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
import hudson.model.AbstractBuild;
import hudson.model.Run;
import hudson.tasks.BuildStep;
import io.jenkins.plugins.opentelemetry.OpenTelemetryAttributesAction;
import io.jenkins.plugins.opentelemetry.OtelUtils;
import io.jenkins.plugins.opentelemetry.job.action.BuildStepMonitoringAction;
import io.jenkins.plugins.opentelemetry.job.action.FlowNodeMonitoringAction;
import io.jenkins.plugins.opentelemetry.job.action.OtelMonitoringAction;
import io.jenkins.plugins.opentelemetry.job.action.RunPhaseMonitoringAction;
import io.jenkins.plugins.opentelemetry.semconv.JenkinsOtelSemanticAttributes;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.trace.Span;
import org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode;
import org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode;
Expand All @@ -34,6 +36,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
Expand All @@ -42,6 +45,8 @@
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import static com.google.common.base.Verify.verifyNotNull;

@Extension
public class OtelTraceService {
private static final Logger LOGGER = Logger.getLogger(OtelTraceService.class.getName());
Expand Down Expand Up @@ -233,9 +238,20 @@ public void putSpan(@NonNull Run run, @NonNull Span span) {

public void putRunPhaseSpan(@NonNull Run run, @NonNull Span span) {
run.addAction(new RunPhaseMonitoringAction(span));
// Phase spans do not get the attributes from the StepContext.
// To ensure that attributes of child spans of the root span are set correctly we read them from an OpenTelemetryAttributesAction set on the Run.
setAttributesToSpan(span, run.getAction(OpenTelemetryAttributesAction.class));
LOGGER.log(Level.FINEST, () -> "putRunPhaseSpan(" + run.getFullDisplayName() + "," + OtelUtils.toDebugString(span) + ")");
}

public void putAgentSpan(@NonNull Run run, @NonNull Span span, @NonNull FlowNode flowNode) {
christophe-kamphaus-jemmic marked this conversation as resolved.
Show resolved Hide resolved
// Agent spans do not get the attributes from the StepContext.
// To ensure that attributes of child spans of the root span are set correctly we read them from an OpenTelemetryAttributesAction set on the Run.
setAttributesToSpan(span, run.getAction(OpenTelemetryAttributesAction.class));
putSpan(run, span, flowNode);
LOGGER.log(Level.FINEST, () -> "putAgentSpan(" + run.getFullDisplayName() + "," + OtelUtils.toDebugString(span) + ")");
}

public void putSpan(@NonNull Run run, @NonNull Span span, @NonNull FlowNode flowNode) {
// FYI for agent allocation, we have 2 FlowNodeMonitoringAction to track the agent allocation duration
flowNode.addAction(new FlowNodeMonitoringAction(span));
Expand All @@ -244,6 +260,17 @@ public void putSpan(@NonNull Run run, @NonNull Span span, @NonNull FlowNode flow
OtelUtils.toDebugString(flowNode) + ", " + OtelUtils.toDebugString(span) + ")");
}

private void setAttributesToSpan(@NonNull Span span, OpenTelemetryAttributesAction openTelemetryAttributesAction) {
if (openTelemetryAttributesAction == null) {
return;
}
for (Map.Entry<AttributeKey<?>, Object> entry : openTelemetryAttributesAction.getAttributes().entrySet()) {
AttributeKey<?> attributeKey = entry.getKey();
Object value = verifyNotNull(entry.getValue());
span.setAttribute((AttributeKey<? super Object>) attributeKey, value);
}
}

static public OtelTraceService get() {
return ExtensionList.lookupSingleton(OtelTraceService.class);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public void notifyOfNewStep(@NonNull Step step, @NonNull StepContext context) {
Computer computer = context.get(Computer.class);
String computerHostname = computer == null ? "#null#" : computer.getHostName();
String computerActions = computer == null ? "#null#" : computer.getAllActions().stream().map(action -> action.getClass().getSimpleName()).collect(Collectors.joining(", "));
String computerName= computer == null ? "#null#" : computer.getName() + "/" + computer.getDisplayName();
String computerName = computer == null ? "#null#" : computer.getName() + "/" + computer.getDisplayName();
log(Level.FINE, () -> run.getFullDisplayName() + " - notifyOfNewStep - Process " + PipelineNodeUtil.getDetailedDebugString(flowNode) + " - computer[name: " + computerName + ", hostname: " + computerHostname + "," + computerActions + "]");
} catch (IOException | InterruptedException | RuntimeException e) {
e.printStackTrace();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright The Original Author or Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.jenkins.plugins.opentelemetry.job.step;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.TaskListener;
import hudson.util.ListBoxModel;
import io.opentelemetry.api.common.AttributeType;
import org.jenkinsci.plugins.workflow.steps.Step;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.logging.Logger;
import java.util.stream.Collectors;

public class SetSpanAttributesStep extends Step {
private final static Logger logger = Logger.getLogger(SetSpanAttributesStep.class.getName());

List<SpanAttribute> spanAttributes;

@DataBoundConstructor
public SetSpanAttributesStep(List<SpanAttribute> spanAttributes) {
this.spanAttributes = spanAttributes;
}

@Override
public StepExecution start(StepContext context) throws Exception {
if (spanAttributes == null) {

Check warning on line 45 in src/main/java/io/jenkins/plugins/opentelemetry/job/step/SetSpanAttributesStep.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 45 is only partially covered, one branch is missing
return new StepExecution(context) {
@Override
public boolean start() {
getContext().onFailure(new IllegalArgumentException("setSpanAttributes requires the spanAttributes parameter"));
return true;

Check warning on line 50 in src/main/java/io/jenkins/plugins/opentelemetry/job/step/SetSpanAttributesStep.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 46-50 are not covered by tests
}
};
}
List<SpanAttribute> noValueSet = spanAttributes.stream().filter(spanAttribute -> spanAttribute.getValue() == null).collect(Collectors.toList());

Check warning on line 54 in src/main/java/io/jenkins/plugins/opentelemetry/job/step/SetSpanAttributesStep.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 54 is only partially covered, one branch is missing
if (!noValueSet.isEmpty()) {

Check warning on line 55 in src/main/java/io/jenkins/plugins/opentelemetry/job/step/SetSpanAttributesStep.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 55 is only partially covered, one branch is missing
String keys = noValueSet.stream().map(SpanAttribute::getKey).reduce("", (accumulator, spanAttribute) -> {
if (accumulator.isEmpty()) {
return spanAttribute;
}
return accumulator + ", " + spanAttribute;
});
// null attributes are NOT supported, log an error
return new StepExecution(context) {
@Override
public boolean start() {
getContext().onFailure(new IllegalArgumentException("setSpanAttributes requires that all spanAttributes have a value set. The attribute(s) with the following keys violate this requirement: " + keys));
return true;

Check warning on line 67 in src/main/java/io/jenkins/plugins/opentelemetry/job/step/SetSpanAttributesStep.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 56-67 are not covered by tests
}
};
}
spanAttributes.forEach(SpanAttribute::setDefaultType);
return new SpanAttributeStepExecution(spanAttributes, context.hasBody(), context);
}

@Extension
public static final class DescriptorImpl extends StepDescriptor {
public static final String FUNCTION_NAME = "setSpanAttributes";

@Override
public Set<? extends Class<?>> getRequiredContext() {
return Collections.singleton(TaskListener.class);
}

@Override
public String getFunctionName() {
return FUNCTION_NAME;
}

@NonNull
@Override
public String getDisplayName() {
return "Set Span Attributes";
}

public ListBoxModel doFillTypeItems(@AncestorInPath Item item, @AncestorInPath ItemGroup context) {
List<AttributeType> supportedAttributeTypes = Arrays.asList(AttributeType.STRING, AttributeType.LONG, AttributeType.BOOLEAN, AttributeType.DOUBLE);
return new ListBoxModel(supportedAttributeTypes.stream().map(t -> new ListBoxModel.Option(t.name(), t.name())).collect(Collectors.toList()));
}

public ListBoxModel doFillTargetItems(@AncestorInPath Item item, @AncestorInPath ItemGroup context) {
return new ListBoxModel(Arrays.stream(SpanAttributeTarget.values()).map(t -> new ListBoxModel.Option(t.name(), t.name())).collect(Collectors.toList()));

Check warning on line 101 in src/main/java/io/jenkins/plugins/opentelemetry/job/step/SetSpanAttributesStep.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 96-101 are not covered by tests
}

}

}
Loading
Loading