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

Propagate trace context in Block Steps #909

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,8 @@
<io.jenkins.plugins.opentelemetry.job.MonitoringRunListener>INFO</io.jenkins.plugins.opentelemetry.job.MonitoringRunListener>
<io.jenkins.plugins.opentelemetry.init.JenkinsExecutorMonitoringInitializer>INFO</io.jenkins.plugins.opentelemetry.init.JenkinsExecutorMonitoringInitializer>
<io.jenkins.plugins.opentelemetry.job.MonitoringRunListener>INFO</io.jenkins.plugins.opentelemetry.job.MonitoringRunListener>
<io.jenkins.plugins.opentelemetry.opentelemetry.ReconfigurableMeterProvider>FINE</io.jenkins.plugins.opentelemetry.opentelemetry.ReconfigurableMeterProvider>
<io.jenkins.plugins.opentelemetry.opentelemetry.ReconfigurableMeterProvider>INFO</io.jenkins.plugins.opentelemetry.opentelemetry.ReconfigurableMeterProvider>
<io.jenkins.plugins.opentelemetry.job.MonitoringPipelineListener>ALL</io.jenkins.plugins.opentelemetry.job.MonitoringPipelineListener>
</loggers>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public class MonitoringAction extends AbstractMonitoringAction implements Action
private transient Run run;

public MonitoringAction(Span span) {
super(span, Collections.emptyList());
super(span, null);
this.rootSpanName = super.getSpanName();
this.rootContext = super.getW3cTraceContext();
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,21 @@ private Iterable<FlowNode> getAncestors(@NonNull final FlowNode flowNode) {
return ancestors;
}

public void closeCurrentScope(@NonNull Run run, @NonNull FlowNode flowNode) {
ImmutableList.copyOf(flowNode.getActions(OtelMonitoringAction.class))
.reverse()
.stream()
.findFirst()
.ifPresentOrElse(OtelMonitoringAction::closeAndPurgeAssociatedScope, () -> {
String msg = "scope to close not found: " + OtelUtils.toDebugString(flowNode) + " in " + run;
if (STRICT_MODE) {
throw new IllegalStateException(msg);
} else {
LOGGER.log(Level.WARNING, msg);
}
});
}

public void removePipelineStepSpanAndCloseAssociatedScopes(@NonNull WorkflowRun run, @NonNull FlowNode flowNode, @NonNull Span span) {
FlowNode startSpanNode;
if (flowNode instanceof AtomNode) {
Expand Down Expand Up @@ -261,9 +276,9 @@ public void putSpan(@NonNull Run run, @NonNull Span span, @NonNull FlowNode flow
OtelUtils.toDebugString(flowNode) + ", " + OtelUtils.toDebugString(span) + ")");
}

public void putSpanAndScopes(@NonNull Run run, @NonNull Span span, @NonNull FlowNode flowNode, List<Scope> scopes) {
public void putSpanAndScope(@NonNull Run run, @NonNull Span span, @NonNull FlowNode flowNode, Scope scope) {
// FYI for agent allocation, we have 2 FlowNodeMonitoringAction to track the agent allocation duration
flowNode.addAction(new FlowNodeMonitoringAction(span, scopes));
flowNode.addAction(new FlowNodeMonitoringAction(span, scope));

LOGGER.log(Level.FINE, () -> "putSpan(" + run.getFullDisplayName() + ", " +
OtelUtils.toDebugString(flowNode) + ", " + OtelUtils.toDebugString(span) + ")");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,14 @@
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;

import java.util.Collections;
import java.util.List;

public abstract class AbstractInvisibleMonitoringAction extends AbstractMonitoringAction {

public AbstractInvisibleMonitoringAction(Span span) {
super(span, Collections.emptyList());
super(span, null);
}

public AbstractInvisibleMonitoringAction(Span span, List<Scope> scopes) {
super(span, scopes);
public AbstractInvisibleMonitoringAction(Span span, Scope scope) {
super(span, scope);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

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

import com.google.common.collect.ImmutableList;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import hudson.model.Action;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
Expand All @@ -18,7 +18,6 @@

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
Expand All @@ -27,7 +26,7 @@
public abstract class AbstractMonitoringAction implements Action, OtelMonitoringAction {
private final static Logger LOGGER = Logger.getLogger(AbstractMonitoringAction.class.getName());

transient SpanAndScopes spanAndScopes;
transient SpanAndScope spanAndScope;


final String traceId;
Expand All @@ -37,20 +36,19 @@

/**
* @param span span of this action
* @param scopes scope and underlying scopes associated with the span.
* @param scope scope and underlying scopes associated with the span.
*/
public AbstractMonitoringAction(Span span, List<Scope> scopes) {
this.spanAndScopes = new SpanAndScopes(span, scopes, Thread.currentThread().getName());
public AbstractMonitoringAction(@NonNull Span span, @Nullable Scope scope) {
this.spanAndScope = new SpanAndScope(span, scope, Thread.currentThread().getName());
this.traceId = span.getSpanContext().getTraceId();
this.spanId = span.getSpanContext().getSpanId();
this.spanName = span instanceof ReadWriteSpan ? ((ReadWriteSpan) span).getName() : null; // when tracer is no-op, span is NOT a ReadWriteSpan
try (Scope scope = span.makeCurrent()) {
Map<String, String> w3cTraceContext = new HashMap<>();
W3CTraceContextPropagator.getInstance().inject(Context.current(), w3cTraceContext, (carrier, key, value) -> carrier.put(key, value));
this.w3cTraceContext = w3cTraceContext;
}

LOGGER.log(Level.FINE, () -> "Span " + getSpanName() + ", thread=" + spanAndScopes.scopeStartThreadName + " opened " + spanAndScopes.scopes.size() + " scopes");
Map<String, String> w3cTraceContext = new HashMap<>();
W3CTraceContextPropagator.getInstance().inject(Context.current().with(span), w3cTraceContext, (carrier, key, value) -> carrier.put(key, value));
this.w3cTraceContext = w3cTraceContext;

LOGGER.log(Level.FINE, () -> "Span " + getSpanName() + ", thread=" + spanAndScope.scopeStartThreadName + " opened " + spanAndScope.scope + " scopes");
}

public String getSpanName() {
Expand All @@ -65,7 +63,7 @@
@Override
@CheckForNull
public Span getSpan() {
return spanAndScopes.span;
return spanAndScope.span;
}

public String getTraceId() {
Expand All @@ -78,13 +76,21 @@

@Override
public void purgeSpanAndCloseAssociatedScopes() {
LOGGER.log(Level.FINE, () -> "Purge span='" + spanName + "', spanId=" + spanId + ", traceId=" + traceId + ": " + spanAndScopes);
Optional.ofNullable(spanAndScopes)
.map(spanAndScopes -> spanAndScopes.scopes)
.map(ImmutableList::copyOf)
.map(ImmutableList::reverse)
.ifPresent(scopes -> scopes.forEach(Scope::close));
this.spanAndScopes = null;
LOGGER.log(Level.FINE, () -> "Purge span='" + spanName + "', spanId=" + spanId + ", traceId=" + traceId + ": " + spanAndScope);
Optional.ofNullable(spanAndScope)
.map(SpanAndScope::getScope)
.ifPresent(Scope::close);
this.spanAndScope = null;
}

@Override
public void closeAndPurgeAssociatedScope() {
LOGGER.log(Level.FINE, () -> "Close and purge scope for span='" + spanName + "', spanId=" + spanId + ", traceId=" + traceId + ": " + spanAndScope);
Optional
.ofNullable(spanAndScope)
.map(SpanAndScope::getScope)
.ifPresent(Scope::close);
this.spanAndScope = new SpanAndScope(Optional.ofNullable(spanAndScope).map(SpanAndScope::getSpan).orElse(Span.getInvalid()), null, "#null#");
}

@Override
Expand All @@ -100,7 +106,7 @@
public boolean hasEnded() {
return
Optional
.ofNullable(spanAndScopes)
.ofNullable(spanAndScope)
.map(sac -> sac.span)
.filter(s -> s instanceof ReadableSpan)
.map(s -> (ReadableSpan) s)
Expand All @@ -109,29 +115,47 @@
}

/**
* Scopes associated with the span and the underlying scopes instantiated to create the span.
* Underlying scopes can be the scope of the underlying wrapping pipeline step (eg a `stage` step).
* Scope associated with the span and the underlying scopes instantiated to create the span.
* Thread name when the scope was opened. Used for debugging, to identify potential leaks.
*/
static class SpanAndScopes {
static class SpanAndScope {
@NonNull
final Span span;
@NonNull
final List<Scope> scopes;
@NonNull
final String scopeStartThreadName;
@Nullable
final Scope scope;
@Nullable
String scopeStartThreadName;

public SpanAndScopes(@NonNull Span span, @NonNull List<Scope> scopes, @NonNull String scopeStartThreadName) {
public SpanAndScope(@NonNull Span span, @Nullable Scope scope, @NonNull String scopeStartThreadName) {
this.span = span;
this.scopes = scopes;
this.scope = scope;
this.scopeStartThreadName = scopeStartThreadName;
}

@NonNull
public Span getSpan() {
return span;
}

@Nullable
public Scope getScope() {
return scope;
}

@Nullable
public String getScopeStartThreadName() {
return scopeStartThreadName;

Check warning on line 147 in src/main/java/io/jenkins/plugins/opentelemetry/job/action/AbstractMonitoringAction.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 147 is not covered by tests
}

public void setScopeStartThreadName(@Nullable String scopeStartThreadName) {
this.scopeStartThreadName = scopeStartThreadName;
}

@Override
public String toString() {
return "SpanAndScopes{" +
"span=" + span +
", scopes=" + scopes.size() +
", scope=" + scope +
", scopeStartThreadName='" + scopeStartThreadName + '\'' +
'}';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;

import java.util.List;

/**
* Span reference associate with a {@link org.jenkinsci.plugins.workflow.graph.FlowNode}
*/
Expand All @@ -19,7 +17,7 @@ public FlowNodeMonitoringAction(Span span) {
super(span);
}

public FlowNodeMonitoringAction(Span span, List<Scope> scopes) {
super(span, scopes);
public FlowNodeMonitoringAction(Span span, Scope scope) {
super(span, scope);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public interface OtelMonitoringAction extends Action {

void purgeSpanAndCloseAssociatedScopes();

void closeAndPurgeAssociatedScope();

/**
* @return {@code true} if the associated {@link Span} has ended
* @see ReadableSpan#hasEnded()
Expand Down

This file was deleted.

Loading