Skip to content

Commit 5d7ed90

Browse files
Bump to OTel SDK 1.46 (#1069)
* Bump to OTel SDK 1.46 * Adapt tests to the new OTel SDK behavior.
1 parent 1bccd39 commit 5d7ed90

22 files changed

+155
-72
lines changed

pom.xml

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@
2323
<jenkins.baseline>2.479</jenkins.baseline>
2424
<jenkins.version>${jenkins.baseline}.1</jenkins.version>
2525
<gitHubRepo>jenkinsci/${project.artifactId}-plugin</gitHubRepo>
26-
<opentelemetry.version>1.44.1</opentelemetry.version>
27-
<jenkins-opentelemetry.version>1.44.1.40.v93f5f8ca_42c3</jenkins-opentelemetry.version>
28-
<opentelemetry-instrumentation.version>2.10.0</opentelemetry-instrumentation.version>
29-
<opentelemetry-semconv.version>1.28.0-alpha</opentelemetry-semconv.version>
30-
<opentelemetry-contrib.version>1.41.0-alpha</opentelemetry-contrib.version>
26+
<opentelemetry.version>1.46.0</opentelemetry.version>
27+
<jenkins-opentelemetry.version>1.46.0.54.v83ff2ff43a_c3</jenkins-opentelemetry.version>
28+
<opentelemetry-instrumentation.version>2.12.0</opentelemetry-instrumentation.version>
29+
<opentelemetry-semconv.version>1.29.0-alpha</opentelemetry-semconv.version>
30+
<opentelemetry-contrib.version>1.44.0-alpha</opentelemetry-contrib.version>
3131
<useBeta>true</useBeta>
3232
<elasticstack.version>8.14.3</elasticstack.version>
3333
<error-prone.version>2.36.0</error-prone.version>
@@ -212,6 +212,21 @@
212212
<groupId>com.fasterxml.jackson.core</groupId>
213213
<artifactId>jackson-core</artifactId>
214214
</exclusion>
215+
<exclusion>
216+
<!-- contrib:1.44 depends on api:1.47 when we pull 1.46 -->
217+
<groupId>io.opentelemetry</groupId>
218+
<artifactId>opentelemetry-api</artifactId>
219+
</exclusion>
220+
<exclusion>
221+
<!-- contrib:1.44 depends on sdk:1.47 when we pull 1.46 -->
222+
<groupId>io.opentelemetry</groupId>
223+
<artifactId>opentelemetry-sdk</artifactId>
224+
</exclusion>
225+
<exclusion>
226+
<!-- contrib:1.44 depends on semconv:1.30 when we pull 1.29-alpha -->
227+
<groupId>io.opentelemetry.semconv</groupId>
228+
<artifactId>opentelemetry-semconv</artifactId>
229+
</exclusion>
215230
</exclusions>
216231
</dependency>
217232
<dependency>
@@ -224,6 +239,16 @@
224239
<groupId>com.fasterxml.jackson.core</groupId>
225240
<artifactId>jackson-core</artifactId>
226241
</exclusion>
242+
<exclusion>
243+
<!-- contrib:1.44 depends on api:1.47 when we pull 1.46 -->
244+
<groupId>io.opentelemetry</groupId>
245+
<artifactId>opentelemetry-api</artifactId>
246+
</exclusion>
247+
<exclusion>
248+
<!-- contrib:1.44 depends on sdk:1.47 when we pull 1.46 -->
249+
<groupId>io.opentelemetry</groupId>
250+
<artifactId>opentelemetry-sdk</artifactId>
251+
</exclusion>
227252
</exclusions>
228253
</dependency>
229254

src/main/java/io/jenkins/plugins/opentelemetry/OpenTelemetryConfiguration.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ public Optional<String> getDisabledResourceProviders() {
8787
public Map<String, String> toOpenTelemetryProperties() {
8888
Map<String, String> properties = new HashMap<>(this.configurationProperties);
8989
if (TESTING_INMEMORY_MODE) {
90-
properties.put(OTEL_TRACES_EXPORTER.asProperty(), "testing");
91-
properties.put(OTEL_METRICS_EXPORTER.asProperty(), "testing");
92-
properties.put(OTEL_METRIC_EXPORT_INTERVAL.asProperty(), "10ms");
90+
properties.putIfAbsent(OTEL_TRACES_EXPORTER.asProperty(), "testing");
91+
properties.putIfAbsent(OTEL_METRICS_EXPORTER.asProperty(), "testing");
92+
properties.putIfAbsent(OTEL_METRIC_EXPORT_INTERVAL.asProperty(), "10ms");
93+
properties.putIfAbsent(OTEL_LOGS_EXPORTER.asProperty(), "none");
9394
} else if (this.getEndpoint().isPresent()) {
9495
this.getEndpoint().ifPresent(endpoint -> { // prepare of Optional.ifPResentOrElse()
9596
properties.compute(OTEL_TRACES_EXPORTER.asProperty(), (key, oldValue) -> {

src/main/java/io/jenkins/plugins/opentelemetry/init/JenkinsExecutorMonitoringInitializer.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ public void postConstruct() {
6969

7070
meter.batchCallback(() -> {
7171
logger.log(Level.FINE, () -> "Recording Jenkins controller executor pool metrics...");
72-
Jenkins jenkins = Jenkins.get();
72+
Jenkins jenkins = Jenkins.getInstanceOrNull();
73+
if (jenkins == null) {
74+
logger.log(Level.FINE, "Jenkins instance is null, skipping executor pool metrics recording");
75+
return;
76+
}
7377

7478
// TOTAL EXECUTORS
7579
AtomicInteger totalExecutorsIdle = new AtomicInteger();

src/main/java/io/jenkins/plugins/opentelemetry/init/PluginMonitoringInitializer.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,18 @@ public void postConstruct() {
6464
meter.batchCallback(() -> {
6565
logger.log(Level.FINE, () -> "Recording Jenkins controller executor pool metrics...");
6666

67+
Jenkins jenkins = Jenkins.getInstanceOrNull();
68+
if (jenkins == null) {
69+
logger.log(Level.FINE, () -> "Jenkins instance is null, skipping plugin monitoring metrics recording");
70+
return;
71+
}
72+
6773
AtomicInteger active = new AtomicInteger();
6874
AtomicInteger inactive = new AtomicInteger();
6975
AtomicInteger hasUpdate = new AtomicInteger();
7076
AtomicInteger isUpToDate = new AtomicInteger();
7177

72-
PluginManager pluginManager = Jenkins.get().getPluginManager();
78+
PluginManager pluginManager = jenkins.getPluginManager();
7379
pluginManager.getPlugins().forEach(plugin -> {
7480
if (plugin.isActive()) {
7581
active.incrementAndGet();

src/main/java/io/jenkins/plugins/opentelemetry/job/MonitoringPipelineListener.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -346,16 +346,14 @@ private void endCurrentSpan(FlowNode node, WorkflowRun run, GenericStatus status
346346
span.setStatus(StatusCode.OK);
347347
} else {
348348
Throwable throwable = errorAction.getError();
349-
if (throwable instanceof FlowInterruptedException) {
350-
FlowInterruptedException interruptedException = (FlowInterruptedException) throwable;
349+
if (throwable instanceof FlowInterruptedException interruptedException) {
351350
List<CauseOfInterruption> causesOfInterruption = interruptedException.getCauses();
352351

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

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

358-
String statusDescription = throwable.getClass().getSimpleName() + ": " + String.join(", ", causeDescriptions);
359357

360358
boolean suppressSpanStatusCodeError = false;
361359
for (CauseOfInterruption causeOfInterruption: causesOfInterruption) {
@@ -365,9 +363,12 @@ private void endCurrentSpan(FlowNode node, WorkflowRun run, GenericStatus status
365363
}
366364
}
367365
if (suppressSpanStatusCodeError) {
368-
span.setStatus(StatusCode.UNSET, statusDescription);
366+
// status.description can't be set for status `unset` as specified by
367+
// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.43.0/specification/trace/api.md#set-status
368+
span.setStatus(StatusCode.UNSET);
369369
} else {
370370
span.recordException(throwable);
371+
String statusDescription = throwable.getClass().getSimpleName() + ": " + String.join(", ", causeDescriptions);
371372
span.setStatus(StatusCode.ERROR, statusDescription);
372373
}
373374
} else {

src/test/java/io/jenkins/plugins/opentelemetry/BaseIntegrationTest.java

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import com.cloudbees.hudson.plugins.folder.computed.FolderComputation;
99
import com.github.rutledgepaulv.prune.Tree;
10+
import edu.umd.cs.findbugs.annotations.NonNull;
1011
import hudson.EnvVars;
1112
import hudson.ExtensionList;
1213
import hudson.model.AbstractBuild;
@@ -20,18 +21,21 @@
2021
import io.jenkins.plugins.opentelemetry.semconv.ExtendedJenkinsAttributes;
2122
import io.opentelemetry.api.GlobalOpenTelemetry;
2223
import io.opentelemetry.api.common.Attributes;
24+
import io.opentelemetry.api.trace.StatusCode;
2325
import io.opentelemetry.sdk.common.CompletableResultCode;
2426
import io.opentelemetry.sdk.metrics.data.MetricData;
2527
import io.opentelemetry.sdk.metrics.data.MetricDataType;
2628
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricExporterProvider;
2729
import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporterProvider;
2830
import io.opentelemetry.sdk.trace.data.SpanData;
31+
import io.opentelemetry.sdk.trace.data.StatusData;
2932
import jenkins.plugins.git.ExtendedGitSampleRepoRule;
3033
import org.hamcrest.CoreMatchers;
3134
import org.hamcrest.MatcherAssert;
3235
import org.hamcrest.Matchers;
3336
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
3437
import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject;
38+
import org.jetbrains.annotations.NotNull;
3539
import org.junit.After;
3640
import org.junit.AfterClass;
3741
import org.junit.Assert;
@@ -41,8 +45,6 @@
4145
import org.junit.Rule;
4246
import org.jvnet.hudson.test.BuildWatcher;
4347

44-
import edu.umd.cs.findbugs.annotations.NonNull;
45-
4648
import java.util.ArrayList;
4749
import java.util.Arrays;
4850
import java.util.Collections;
@@ -61,7 +63,8 @@
6163
import java.util.stream.Collectors;
6264

6365
import static com.google.common.base.Verify.verify;
64-
import static java.util.Optional.*;
66+
import static java.util.Optional.empty;
67+
import static java.util.Optional.of;
6568
import static org.junit.Assert.fail;
6669

6770
public class BaseIntegrationTest {
@@ -139,7 +142,11 @@ protected void checkChainOfSpans(Tree<SpanDataWrapper> spanTree, String... expec
139142
Assert.fail("No element in the list of expected spans for " + Arrays.asList(expectedSpanNames));
140143
}
141144
final String leafSpanName = expectedSpanNamesIt.next();
142-
Optional<Tree.Node<SpanDataWrapper>> actualNodeOptional = spanTree.breadthFirstSearchNodes(node -> Objects.equals(leafSpanName, node.getData().spanData.getName()));
145+
Optional<Tree.Node<SpanDataWrapper>> actualNodeOptional = spanTree.breadthFirstSearchNodes(
146+
node -> {
147+
LOGGER.log( Level.FINE, () -> "Compare '" + leafSpanName + "' with '" + node.getData().spanData.getName() + "'");
148+
return Objects.equals(leafSpanName, node.getData().spanData.getName());
149+
});
143150

144151
MatcherAssert.assertThat("Expected leaf span '" + leafSpanName + "' in chain of span" + expectedSpanNamesList + " not found", actualNodeOptional.isPresent(), CoreMatchers.is(true));
145152

@@ -178,11 +185,16 @@ protected void dumpMetrics(Map<String, MetricData> exportedMetrics) {
178185
}).collect(Collectors.joining(" \n")));
179186
}
180187

181-
protected Tree<SpanDataWrapper> getGeneratedSpans() {
182-
return getGeneratedSpans(0);
188+
protected Tree<SpanDataWrapper> getBuildTrace() {
189+
return getBuildTrace(0);
190+
}
191+
192+
protected Tree<SpanDataWrapper> getBuildTrace(int traceIndex) {
193+
String rootSpanPrefix = ExtendedJenkinsAttributes.CI_PIPELINE_RUN_ROOT_SPAN_NAME_PREFIX;
194+
return getTrace(rootSpanPrefix, traceIndex);
183195
}
184196

185-
protected Tree<SpanDataWrapper> getGeneratedSpans(int index) {
197+
protected static @NotNull Tree<SpanDataWrapper> getTrace(String rootSpanPrefix, int traceIndex) {
186198
CompletableResultCode completableResultCode = jenkinsControllerOpenTelemetry.getOpenTelemetrySdk().getSdkTracerProvider().forceFlush();
187199
completableResultCode.join(1, TimeUnit.SECONDS);
188200
List<SpanData> spans = InMemorySpanExporterProvider.LAST_CREATED_INSTANCE.getFinishedSpanItems();
@@ -192,16 +204,23 @@ protected Tree<SpanDataWrapper> getGeneratedSpans(int index) {
192204
final SpanData spanData2 = spanDataNode2.getData().spanData;
193205
return Objects.equals(spanData1.getSpanId(), spanData2.getParentSpanId());
194206
};
195-
final List<Tree<SpanDataWrapper>> trees = Tree.of(spans.stream().map(span -> new SpanDataWrapper(span)).collect(Collectors.toList()), parentChildMatcher);
207+
final List<Tree<SpanDataWrapper>> trees = Tree.of(spans.stream().map(SpanDataWrapper::new).collect(Collectors.toList()), parentChildMatcher);
196208
System.out.println("## TREE VIEW OF SPANS ## ");
197209
for (Tree<SpanDataWrapper> tree : trees) {
198210
System.out.println(tree);
199211
}
200212

201-
if (index < 0 || index >= trees.size()) {
202-
throw new IllegalArgumentException("No span found for index=" + index + ", trees.size()=" + trees.size());
213+
int currentBuildIndex = 0;
214+
for (Tree<SpanDataWrapper> tree : trees) {
215+
if (tree.asNode().getData().spanData.getName().startsWith(rootSpanPrefix)) {
216+
if (currentBuildIndex == traceIndex) {
217+
return tree;
218+
} else {
219+
currentBuildIndex++;
220+
}
221+
}
203222
}
204-
return trees.get(index);
223+
throw new IllegalArgumentException("No root span found starting with " + rootSpanPrefix + " and index " + traceIndex);
205224
}
206225

207226
protected void assertEnvironmentVariables(EnvVars environment) {
@@ -345,6 +364,13 @@ public String toString() {
345364
if (attributes.get(ExtendedJenkinsAttributes.JENKINS_STEP_ID) != null) {
346365
result += ", node.id: " + attributes.get(ExtendedJenkinsAttributes.JENKINS_STEP_ID);
347366
}
367+
StatusData status = spanData.getStatus();
368+
if (status != null && status.getStatusCode() != StatusCode.UNSET) {
369+
result += ", status.code: " + status.getStatusCode();
370+
}
371+
if (status != null && status.getDescription() != null && !status.getDescription().isEmpty()) {
372+
result += ", status.description: " + status.getDescription();
373+
}
348374
return result;
349375
}
350376
}

src/test/java/io/jenkins/plugins/opentelemetry/JenkinsOtelPluginFreestyleIntegrationTest.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public void testFreestyleJob() throws Exception {
5454

5555
String rootSpanName = ExtendedJenkinsAttributes.CI_PIPELINE_RUN_ROOT_SPAN_NAME_PREFIX + jobName;
5656

57-
Tree<SpanDataWrapper> spans = getGeneratedSpans();
57+
Tree<SpanDataWrapper> spans = getBuildTrace();
5858
checkChainOfSpans(spans, "Phase: Start", rootSpanName);
5959
checkChainOfSpans(spans, "shell", "Phase: Run", rootSpanName);
6060
checkChainOfSpans(spans, "Phase: Finalise", rootSpanName);
@@ -76,7 +76,7 @@ public void testFreestyleJob_with_multiple_builders() throws Exception {
7676

7777
String rootSpanName = ExtendedJenkinsAttributes.CI_PIPELINE_RUN_ROOT_SPAN_NAME_PREFIX + jobName;
7878

79-
Tree<SpanDataWrapper> spans = getGeneratedSpans();
79+
Tree<SpanDataWrapper> spans = getBuildTrace();
8080
checkChainOfSpans(spans, "Phase: Start", rootSpanName);
8181
checkChainOfSpans(spans, "shell", "Phase: Run", rootSpanName);
8282
checkChainOfSpans(spans, "shell", "Phase: Run", rootSpanName);
@@ -98,7 +98,7 @@ public void testFreestyleJobFailed() throws Exception {
9898

9999
String rootSpanName = ExtendedJenkinsAttributes.CI_PIPELINE_RUN_ROOT_SPAN_NAME_PREFIX + jobName;
100100

101-
Tree<SpanDataWrapper> spans = getGeneratedSpans();
101+
Tree<SpanDataWrapper> spans = getBuildTrace();
102102
checkChainOfSpans(spans, "Phase: Start", rootSpanName);
103103
checkChainOfSpans(spans, "shell", "Phase: Run", rootSpanName);
104104
checkChainOfSpans(spans, "Phase: Finalise", rootSpanName);
@@ -122,7 +122,7 @@ public void testFreestyleJob_with_publishers() throws Exception {
122122

123123
String rootSpanName = ExtendedJenkinsAttributes.CI_PIPELINE_RUN_ROOT_SPAN_NAME_PREFIX + jobName;
124124

125-
Tree<SpanDataWrapper> spans = getGeneratedSpans();
125+
Tree<SpanDataWrapper> spans = getBuildTrace();
126126
checkChainOfSpans(spans, "Phase: Start", rootSpanName);
127127
checkChainOfSpans(spans, "shell", "Phase: Run", rootSpanName);
128128
checkChainOfSpans(spans, "archiveArtifacts", "Phase: Run", rootSpanName);
@@ -148,7 +148,7 @@ public void testFreestyleJob_with_assigned_node() throws Exception {
148148

149149
String rootSpanName = ExtendedJenkinsAttributes.CI_PIPELINE_RUN_ROOT_SPAN_NAME_PREFIX + jobName;
150150

151-
Tree<SpanDataWrapper> spans = getGeneratedSpans();
151+
Tree<SpanDataWrapper> spans = getBuildTrace();
152152
checkChainOfSpans(spans, "Phase: Start", rootSpanName);
153153
checkChainOfSpans(spans, "shell", "Phase: Run", rootSpanName);
154154
checkChainOfSpans(spans, "Phase: Finalise", rootSpanName);
@@ -169,7 +169,7 @@ public void testFreestyleJob_with_causes() throws Exception {
169169
FreeStyleProject project = jenkinsRule.createFreeStyleProject(jobName);
170170
project.getBuildersList().add(new Shell("set -u && touch \"x\""));
171171
jenkinsRule.assertBuildStatusSuccess(project.scheduleBuild2(0, new Cause.UserIdCause()));
172-
Tree<SpanDataWrapper> spans = getGeneratedSpans();
172+
Tree<SpanDataWrapper> spans = getBuildTrace();
173173
List<SpanDataWrapper> root = spans.byDepth().get(0);
174174
Attributes attributes = root.get(0).spanData.getAttributes();
175175
MatcherAssert.assertThat(attributes.get(ExtendedJenkinsAttributes.CI_PIPELINE_RUN_CAUSE), CoreMatchers.is(List.of("UserIdCause:SYSTEM")));
@@ -192,7 +192,7 @@ public void testFreestyleJob_with_ant_plugin() throws Exception {
192192

193193
String rootSpanName = ExtendedJenkinsAttributes.CI_PIPELINE_RUN_ROOT_SPAN_NAME_PREFIX + jobName;
194194

195-
Tree<SpanDataWrapper> spans = getGeneratedSpans();
195+
Tree<SpanDataWrapper> spans = getBuildTrace();
196196
checkChainOfSpans(spans, "Phase: Start", rootSpanName);
197197
checkChainOfSpans(spans, "ant", "Phase: Run", rootSpanName);
198198
checkChainOfSpans(spans, "Phase: Finalise", rootSpanName);
@@ -227,21 +227,21 @@ public void testFreestyleJob_with_culprits() throws Exception {
227227

228228
String rootSpanName = ExtendedJenkinsAttributes.CI_PIPELINE_RUN_ROOT_SPAN_NAME_PREFIX + jobName;
229229

230-
Tree<SpanDataWrapper> spans = getGeneratedSpans();
230+
Tree<SpanDataWrapper> spans = getBuildTrace();
231231
checkChainOfSpans(spans, "Phase: Run", rootSpanName);
232232

233233
// 2nd build
234234
scm.addChange().withAuthor("bob");
235235
project.getBuildersList().add(new FailureBuilder());
236236
jenkinsRule.assertBuildStatus(Result.FAILURE, project.scheduleBuild2(0).get());
237-
spans = getGeneratedSpans(1);
237+
spans = getBuildTrace(1);
238238
checkChainOfSpans(spans, "Phase: Run", rootSpanName);
239239

240240
// 3rd build. bob continues to be in culprit
241241
project.getBuildersList().add(new FailureBuilder());
242242
scm.addChange().withAuthor("charlie");
243243
FreeStyleBuild build = project.scheduleBuild2(0).get();
244-
spans = getGeneratedSpans(2);
244+
spans = getBuildTrace(2);
245245

246246
checkChainOfSpans(spans, "Phase: Run", rootSpanName);
247247

0 commit comments

Comments
 (0)